Rust 追踪工厂和产品之间所有权的惯用方法是什么?

Rust 追踪工厂和产品之间所有权的惯用方法是什么?,rust,factory,Rust,Factory,我有一个类,它创建另一个类的实例。有时,它需要对其产品做出反应或以其他方式使用其产品。然而,如果它通过一个它不拥有的产品,它可能会制造麻烦。我有以下解决方案: struct Parent { id: Option<*const Parent>, name: String, } impl Parent { fn new(name: String) -> Parent { Parent { id : None,

我有一个类,它创建另一个类的实例。有时,它需要对其产品做出反应或以其他方式使用其产品。然而,如果它通过一个它不拥有的产品,它可能会制造麻烦。我有以下解决方案:

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,
    name: String,
}

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 {}.",parent_one.name,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 {}.",parent_two.name,child_two.name);
    }   
}
结构父级{ id:选项, 名称:String, } 内隐父代{ fn新建(名称:字符串)->父级{ 母公司{ id:没有, 姓名:姓名,, } } fn spawn(&mut self,名称:String)->Child{ 如果let None=self.id{ self.id=Some(self作为*常量父项); } 孩子{ 父项:self.id.unwrap(), 名称 } } fn是\u父对象(&self,子对象:&child)->bool{ 如果self.id.unwrap()==child.parent{ 真的 }否则{ 假的 } } } 结构子对象{ 父项:*常量父项, 名称:String, } fn main(){ 让mut parent_one=parent::new(字符串::from(“Bob”); 让mut parent_two=parent::new(字符串::from(“Ben”); 让child_one=parent_one.spawn(字符串::from(“Barry”)); 让child_two=parent_two.spawn(字符串::from(“Bishop”); 如果父对象是父对象(&子对象){ println!(“{}是{}的父对象。”,父对象名称,子对象名称); } 如果父项为父项(子项为二){ println!(“{}是{}”的父项,父项为one.name,子项为two.name); } 如果父项为父项(子项为一){ println!(“{}是{}的父项。”,父项2.name,子项1.name); } 如果父项是父项(&child){ println!(“{}是{}的父对象。”,父对象名称,子对象名称); } } 我担心的第一件事是父母被销毁,地址被重新使用。也许需要一个时间戳来进一步确保所有权

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

编辑:


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

编辑2:


我已经确认,重用内存地址肯定是一个问题。另外,虽然时间戳是有用的,但没有随机性,我担心它仍然不够。

如果您愿意采用“全局递增ID”方法,那么您需要的是一个
ID
类型,具有这样的接口,您可以将其存储在
类型中:

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 { ... } }
此解决方案唯一的微妙之处是
PhantomData
字段。它的存在是为了强制
Id
不执行
Send
Sync
,因此对
Id
的任何引用都仅限于创建它的线程。使用
#![feature(negative_impls)]
您可以使用更清晰的
impl解决方案!发送/!同步Id{}
,但这是不稳定的,因此我们只需添加一个不是
Send
Sync
的伪字段(因为原始指针不是Send/Sync,并且
PhantomData
共享其参数的
Send
/
Sync
状态)

另见:

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

#[derive(Copy, Clone, Eq, 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)
   }
}
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)
}
您尚未指出您是否依赖于
is_child
检查来确保内存安全或逻辑正确性。如果只是后者,您可以通过保留
u64::MAX
来表示“我们没有Id”,并通过以下方式实现
Id::new

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)
}
由于恐慌可以在Rust中恢复(也可以运行析构函数),并且上述仅在
NEXT_PARENT
增加后检测溢出,因此在这种情况下,可能会在恐慌期间或之后创建两个相等的
Id
s。如果这可能违反内存安全,您有两种选择:要么将panic更改为
std::process::abort
,要么使用比较和交换循环检查溢出,避免实际增加计数器,如下所示:

#[derive(Copy, Clone, Eq, 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)
   }
}
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)
}

显然,此循环可能会带来较小的性能损失。最后,请记住,
AtomicU64
是广泛可用的,但不是普遍可用的。如果可移植性是一个问题,请参阅并考虑使用<代码> AtomicUsize <代码>(虽然这可能显著增加在32位平台上溢出溢出的几率)。为什么不给每一个家长分配一个递增的整数ID,让孩子记住家长的ID呢?这只是一个简单的例子。完整的代码是一个链表库。我将提交一个编辑。是的,必须有更好的方法来做到这一点。在编译时进行检查是最好的。我甚至不能开始考虑如何在编译时进行检查。在C++中,我知道可以简单地将一个静态变量作为ID增加。但是,我避免使用外部库和不安全代码。code>Rc
Arc
有它们自己的
引用,可以从子级使用它们来指向其父级。您可能希望将函数更改为只接受
Rc
/
Arc
wrapped
self