微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

跟踪工厂及其产品之间所有权的惯用方法是什么?

如何解决跟踪工厂及其产品之间所有权的惯用方法是什么?

我有一个类可以创建另一个类的实例。有时它需要对其产品做出反应或以其他方式使用它。但是,如果将不属于它的产品传递给它,则可能会造成麻烦。我有以下解决方案:

struct Parent {
    id: Option<*const Parent>,name: String,}
impl Parent {
    fn new(name: String) -> Parent {
        Parent {
            id : None,name : name,}
    }
    fn spawn(&mut self,name: String) -> Child {
        if let None = self.id {
            self.id = Some(self as *const Parent);
        }
        Child {
            parent: self.id.unwrap(),name,}
    }
    fn is_parent(&self,child: &Child) -> bool {
        if self.id.unwrap() == child.parent {
            true
        } else {
            false
        }
    }
}
struct Child {
    parent: *const Parent,}

fn main() {
    let mut parent_one = Parent::new(String::from("Bob"));
    let mut parent_two = Parent::new(String::from("Ben"));
    let child_one = parent_one.spawn(String::from("Barry"));
    let child_two = parent_two.spawn(String::from("Bishop"));
    if parent_one.is_parent(&child_one) {
        println!("{} is the parent of {}.",parent_one.name,child_one.name);
    }
    if parent_one.is_parent(&child_two) {
        println!("{} is the parent of {}.",child_two.name);
    }   
    if parent_two.is_parent(&child_one) {
        println!("{} is the parent of {}.",parent_two.name,child_one.name);
    }
    if parent_two.is_parent(&child_two) {
        println!("{} is the parent of {}.",child_two.name);
    }   
}

我担心的第一件事是父级被销毁并且地址被重用。也许需要一个时间戳来进一步确保所有权?

更进一步,我想知道 Rust 是否有更好的方法来处理这种情况?

编辑:

这只是一个最小的例子。完整的代码一个链表库。目前我正在创建一个允许从列表中删除节点的函数。这是通过调用 List.remove(Node) 来完成的。但是,我需要确保该节点确实属于提供的列表。因为,如果您移除头部、尾部或最后一个元素,则必须更新 List。如果您提供的 List 和 Node 不匹配,则最终结果将不正确。

编辑 2:

我已经确认,重用内存地址肯定是有问题的。此外,虽然时间戳很有帮助,但如果没有随机化,我担心它仍然不够。

解决方法

如果您愿意采用“全局递增 ID”方法,您需要的是一个 Id 类型,它具有这样的接口,您可以将其存储在 Parent 和 {{1} } 类型:

Child

有几种方法可以实现这种类型,具体取决于您的需要。如果您永远不需要在线程之间移动或共享 pub struct Id(...); impl Id { pub fn new() -> Id { ... } } impl PartialEq<Id> for Id { fn eq(&self,other: &Id) -> bool { ... } } impl Eq for Id { } impl Clone for Id { fn clone(&self) -> Id { ... } } Parent,您可以使用一个简单的线程本地计数器:

Child

此解决方案的唯一微妙之处在于 #[derive(Copy,Clone,Eq,PartialEq)] pub struct Id(u64,PhantomData<*mut ()>); impl Id { pub fn new() -> Id { thread_local! (static NEXT_ID: Cell<u64> = Cell::new(0)); let id = NEXT_ID.with(|cell| { let id = cell.get(); cell.set(id.checked_add(1).expect("Ran out of IDs!")); id }); Id(id,PhantomData) } } 字段。它的存在是为了强制 PhantomData<*mut ()> 不实现 IdSend,以便对 Sync 的任何引用仍然局限于创建它的线程。使用 Id,您可以使用更清晰的 #![feature(negative_impls)] 解决方案,但这是不稳定的,因此我们只需添加一个不是 impl !Send/!Sync for Id { }Send 的虚拟字段(因为原始指针不是发送/同步,Sync 共享其参数的 PhantomData/Send 状态。)

另见:PhantomData in the Rust nomiconSend and Sync in the Rust nomicon

如果您确实需要从多个线程访问 SyncParent,事情会变得有点复杂。我们需要将下一个 ID 存储在全局原子整数变量中,但问题是没有“原子检查添加”这样的东西,因此我们无法像在单线程情况下那样简单地检测 ID 包装。代码看起来像这样:

Child

您没有说明您是依靠 #[derive(Copy,PartialEq)] pub struct Id(u64); impl Id { pub fn new() -> Id { static NEXT_ID: AtomicU64 = AtomicU64::new(0); let id = todo!(); // what goes here? Id(id) } } 检查来确保内存安全还是仅仅依靠逻辑正确性。如果只是后者,您可以通过保留 is_child 来表示“我们的 ID 用完了”并以这种方式实现 u64::MAX

Id::new

由于恐慌在 Rust 中是可以恢复的(也可以运行析构函数),并且上面只检测溢出之后 pub fn new() -> Id { static NEXT_ID: AtomicU64 = AtomicU64::new(0); let id = NEXT_ID.fetch_add(1,Ordering::Relaxed); if id == u64::MAX { panic!("Ran out of IDs!"); } Id(id) } 已经增加,在这种情况下,两个相等的 {{ 1}} 在恐慌期间或之后创建。如果这可能违反内存安全,您有两种选择:将恐慌更改为 NEXT_PARENT,或者使用比较和交换循环来检查溢出并避免实际增加计数器,如下所示:

Id

显然,这个循环可能会带来小的性能损失。最后,请记住,std::process::abort 是广泛可用的,但并非普遍可用。如果考虑可移植性,请参阅 std::sync::atomic docs 并考虑使用 pub fn new() -> Id { static NEXT_ID: AtomicU64 = AtomicU64::new(0); let id = loop { let id = NEXT_ID.load(Ordering::Relaxed); if id == u64::MAX { panic!("Ran out of IDs!"); if let Ok(_) = NEXT_ID.compare_exchange(id,id + 1,Ordering::Relaxed,Ordering::Relaxed) { break id } // another thread changed NEXT_ID after we checked for overflow,try again }; Id(id) } (尽管这可能会显着增加在 32 位平台上发生溢出恐慌的几率)。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。