如何创建一个可以链接到其自身另一个实例的 Rust 结构?

问题描述 投票:0回答:1

我正在尝试创建一个结构体实例链,如下所示:

Struct_instance_a:
id:1
prev:None

Struct_instance_b:
id:2
prev:Struct_instance_a

etc..

但是从编译器中得到这个错误:

error[E0382]: borrow of moved value: `a`
error[E0382]: borrow of moved value: `a`
  --> src/main.rs:31:9
   |
28 |         let a = crate::Node::new(None, 1);
   |             - move occurs because `a` has type `Node`, which does not implement the `Copy` trait
29 |         let b=crate::Node::new(Some(a), 2);
   |                                     - value moved here
30 |
31 |         assert_eq!(a.id, b.prev.unwrap().id);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move

这是我的代码片段:

fn main() {
    println!("hello");
}

pub struct Node {
    prev: Option<Box<Node>>,
    id: u64,
}

impl Node {
    fn new(prev: Option<Node>, id:u64) -> Node {
        Node {
            prev: match prev {
                None => None,
                Some(prev) => Some(Box::new(prev))
            },
            id
        }
    }
}


#[cfg(test)]
mod tests {

    #[test]
    fn test_children() {
        let a = crate::Node::new(None, 1);
        let b = crate::Node::new(Some(a), 2);

        assert_eq!(a.id, b.prev.unwrap().id);
    }

}

我是 Rust 新手,试图理解为什么这不起作用。到目前为止,我已经阅读了有关所有权、借用和生命周期的内容,并尝试了这些内容的一些组合。感觉我已经接近解决方案,但还缺少一些基本知识。

rust ownership
1个回答
0
投票

当您执行

Node::new(some(a), 2)
时,a将不再可访问,它的所有权现在属于
b

这就是为什么,当您稍后执行

a.id
时,它会显示
value borrowed here after move
,您将所有权移至
b
,因此您无法执行
a.id
。访问
a
的唯一方法是通过
b.prev.unwrap()
(这就是您所做的。

如果您希望节点

a
可以从
a
b.prev
访问,有很多方法可以做到这一点。

其中一种方法涉及根本不移动该值(因此

b
从一开始就不拥有
a
的所有权)。这可以通过参考来完成:

fn main() {
    println!("hello");
}

pub struct Node<'inner_node> {
    prev: Option<&'inner_node Node<'inner_node>>,
    id: u64,
}

impl<'inner_node> Node<'inner_node> {
    fn new<'a>(prev: Option<&'a Node<'a>>, id: u64) -> Node<'a> {
        Node {
            prev,
            id
        }
    }
}


#[cfg(test)]
mod tests {

    #[test]
    fn test_children() {
        let a = crate::Node::new(None, 1);
        let b = crate::Node::new(Some(&a), 2);

        assert_eq!(a.id, b.prev.unwrap().id);
    }

}

这引入了生命周期注释,这可能不是您想要的。在这种情况下,您可以使用

RC
结构,它是一个智能指针,允许一个值拥有多个所有者,但需要进行一些运行时检查:

use std::rc::Rc;

fn main() {
    println!("hello");
}

pub struct Node {
    prev: Option<Rc<Node>>,
    id: u64,
}

impl Node {
    fn new(prev: Option<Rc<Node>>, id: u64) -> Node {
        Node {
            prev,
            id
        }
    }
}


#[cfg(test)]
mod tests {
    use std::rc::Rc;


    #[test]
    fn test_children() {
        let a = Rc::new(crate::Node::new(None, 1));
        let b = crate::Node::new(Some(a.clone()), 2);

        assert_eq!(a.id, b.prev.unwrap().id);
    }

}

请注意,为了实现这一点,我们正在做

a.clone()
。这会克隆 RC 指针,但不会克隆节点。所以现在存在 2 个指针(
a
b.prev
),但只有 1 个(不包括
b
本身)节点。

RC 的缺点之一是现在里面的值是不可变的。如果您需要改变节点,它们必须是

Rc<RefCell<Node>>
而不是
Rc<Node>

RefCell
是另一个智能指针,就像
Rc
一样。其中
Rc
允许一个值拥有多个所有者,
RefCell
允许获取对内部对象的可变引用(通过运行时检查),即使
RefCell
本身不可变。通过组合
Rc
RefCell
,您可以拥有多个可以改变值的所有者。

© www.soinside.com 2019 - 2024. All rights reserved.