“Cell”和“RefCell”

Cell and RefCell implement what Rust calls interior mutability: mutation of values in an immutable context.

“Cell”通常用于简单类型,因为它需要复制或移动值。更复杂的内部类型通常使用“RefCell”,它会在运行时跟踪已共享和专有的引用,并在这些引用被滥用时 panic。

use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug, Default)]
struct Node {
    value: i64,
    children: Vec<Rc<RefCell<Node>>>,
}

impl Node {
    fn new(value: i64) -> Rc<RefCell<Node>> {
        Rc::new(RefCell::new(Node { value, ..Node::default() }))
    }

    fn sum(&self) -> i64 {
        self.value + self.children.iter().map(|c| c.borrow().sum()).sum::<i64>()
    }
}

fn main() {
    let root = Node::new(1);
    root.borrow_mut().children.push(Node::new(5));
    let subtree = Node::new(10);
    subtree.borrow_mut().children.push(Node::new(11));
    subtree.borrow_mut().children.push(Node::new(12));
    root.borrow_mut().children.push(subtree);

    println!("graph: {root:#?}");
    println!("graph sum: {}", root.borrow().sum());
}
  • 在此示例中,如果我们使用的是“Cell”而非“RefCell”,则必须将“Node”从“Rc”中移出以推送子项,然后再将其移回原位。这是安全的做法,因为单元格中总是有一个未引用的值,但这不符合人体工程学。
  • 如需使用 Node 执行任何操作,您必须调用“RefCell”方法,通常为“borrow”或“borrow_mut”。
  • 演示可以通过向“subtree.children”添加“root”来创建引用循环(不要尝试输出它!)。
  • 为了演示运行时 panic,请添加一个会递增“self.value”并以相同方法调用其子项的“fn inc(&mut self)”。如果存在引用循环,就会 panic,并且“thread”“main”会因“already borrowed: BorrowMutError”而 panic。