嵌套结构与引用

时间:2016-09-26 14:47:38

标签: nested rust

我有一些嵌套的结构,无法创建父结构的后引用。 example

struct Foo<'a> {
    parent: &'a Bar<'a>,
}

impl<'a> Foo<'a> {
    fn new(parent: &'a Bar) -> Self {
        Foo { parent: parent }
    }

    fn hello_world(&self) -> String {
        self.parent.hello().to_owned() + " world"
    }
}

struct Bar<'b> {
    child: Option<Foo<'b>>,
    data: &'static str,
}

impl<'b> Bar<'b> {
    fn new() -> Self {
        Bar {
            child: None,
            data: "hello",
        }
    }

    fn hello(&self) -> &str {
        self.data
    }

    fn get_foo(&self) -> Option<&Foo> {
        self.child.as_ref()
    }
}

fn main() {
    let bar = Bar::new();
    assert_eq!("hello", bar.hello());
    match bar.get_foo() {
        Some(foo) => assert_eq!("hello world", foo.hello_world()),
        None => (),
    }
}

如何将None替换为Some<Foo>并引用Bar?到目前为止,我不确定这是可能的。

2 个答案:

答案 0 :(得分:1)

这不是您的示例的简单解决方案,但我相信您可以使用ArcRwLock创建“循环引用”。 API不完全相同(例如,parent是一个可选字段),我重命名了一些对象,它肯定更详细,但是你的测试通过了!

use std::sync::{Arc, RwLock};

#[derive(Debug, Clone)]
struct Child {
    parent: Option<Arc<RwLock<Parent>>>
}

impl Child {
    fn new() -> Self {
        Child {
            parent: None
        }
    }

    fn hello_world(&self) -> String {
        let x = self.parent.as_ref().unwrap().clone();
        let y = x.read().unwrap();
        y.hello().to_owned() + " world"
    }
}

#[derive(Debug, Clone)]
struct Parent {
    child: Option<Arc<RwLock<Child>>>,
    data: &'static str
}

impl Parent {
    fn new() -> Self {
        Parent {
            child: None,
            data: "hello"
        }
    }

    fn hello(&self) -> &str {
        self.data
    }

    fn get_child(&self) -> Option<Arc<RwLock<Child>>> {
        self.child.as_ref().map(|x| x.clone() )
    }


}

fn main() {
    let parent = Arc::new(RwLock::new(Parent::new()));
    let child = Arc::new(RwLock::new(Child::new()));

    parent.write().unwrap().child = Some(child.clone());
    child.write().unwrap().parent = Some(parent.clone());

    assert_eq!("hello", parent.read().unwrap().hello());

    {
        let x = parent.read().unwrap();
        match x.get_child() {
            Some(child) => { assert_eq!("hello world", child.read().unwrap().hello_world()); }
            None => {},
        }
    }

}

答案 1 :(得分:1)

我有一个类似的问题,对建议的解决方案并不完全满意。

如果您的结构确实是嵌套的(即,您有一个“父母”和“孩子”的概念,每个孩子都有一个唯一的父母),那么父母应该拥有该孩子是很自然的事情。因此,使用Rc / Arc(旨在允许多个所有者使用)看起来不是正确的解决方案-尤其如此,因为@Shepmaster指出,它“鼓励”(或至少允许)创建循环引用。

我的想法是让每个孩子都拥有指向其父母的原始指针:

pub struct Node {
    parent: *mut Node,
    // ...
}

由于节点是由其父节点拥有的,因此只能在借用其父节点(分别以可变方式借用)时借用(相应地可变借用)。因此,在这种情况下,将self.parent强制转换为&Node(如果&mut Node是可变的,则分别为self)应该是安全的。

impl Node {
    pub fn get_parent(&self) -> Option<&Node> {
        unsafe { self.parent.as_ref() }
    }

    pub fn get_mut_parent(&mut self) -> Option<&mut Node> {
        unsafe { self.parent.as_mut() }
    }
}

但是,这要求父节点的地址永不更改(父节点永不移动)。这可以通过仅处理 boxed 节点来实现。

pub struct Node {
    parent: *mut Node,
    children: Vec<Box<Node>>,
    // ..
}

impl Node {
    pub fn new(data: &str) -> Box<Node> {
        Box::new(Node {
            parent: std::ptr::null_mut(),
            children: vec![],
            // ..
        })
    }

    pub fn append_child(&mut self, mut child: Box<Node>) -> usize {
        child.parent = self;
        self.children.push(child);
        self.children.len() - 1
    }
}

我实现了一个完整的示例in the playground