rust - 无法可变地借用 RefCell 内的内容,即使在前一个可变借用的范围已经结束之后

标签 rust

考虑下面的代码

use std::{cell::RefCell, rc::Rc};

type NodeRef = Rc<RefCell<_Node>>;

#[derive(Debug)]
struct _Node {
    id: usize,
    data: Option<NodeRef>,
    edges: Vec<NodeRef>,
}

impl _Node {
    fn add(&mut self, other: NodeRef) {
        println!("at {}", self.id);

        self.data = match self.data.take() {
            Some(current_data) => {
                {
                    let mut current_data_raw = current_data.borrow_mut();
                    current_data_raw.id += other.borrow().id;
                }
                Some(current_data)
            }
            None => Some(Rc::clone(&other)),
        };

        for e in &self.edges {
            e.borrow_mut().add(Rc::clone(&other));
        }

        println!("done {}", self.id);
    }
}

#[derive(Debug)]
struct Node(NodeRef);

impl Node {
    fn new(id: usize) -> Node {
        Node(Rc::new(RefCell::new(_Node {
            id,
            data: None,
            edges: vec![],
        })))
    }

    fn add_edge(&self, other: &Node) {
        self.0.borrow_mut().edges.push(Rc::clone(&other.0));
    }

    fn add(&self, other: Self) {
        self.0.borrow_mut().add(other.0);
    }
}

fn main() {
    let a = Node::new(0);
    let b = Node::new(1);
    let c = Node::new(2);
    let d = Node::new(3);
    let e = Node::new(4);
    let f = Node::new(5);

    d.add_edge(&a);
    d.add_edge(&b);

    e.add_edge(&b);
    e.add_edge(&c);

    f.add_edge(&d);
    f.add_edge(&e);

    f.add(Node::new(6));
}

运行时生成的输出是

at 5
at 3
at 0
done 0
at 1
done 1
done 3
at 4
at 1
thread 'main' panicked at 'already mutably borrowed: BorrowError', src/libcore/result.rs:1009:5

这将创建一个图形

F--E--A 
 \  \
  \   B
   \ /
    D
     \ 
      C

我试图在整个图中传播一个值,所以它从 F 开始,然后到达 E 和 D。从 E 到 A 和 B,没有任何错误。然后从 D 开始,运行时 panic 说可变借用的 RefCell 约束已被打破。

它似乎正在考虑从先前使用 B 调用回调的可变借用,然而,_Node::add 中的可变借用 (current_data_raw) 有一个有限的范围,在范围结束后,我应该被允许再次可变地借用该值。从输出来看,当第二次为节点 B 调用该函数时,该函数的整个第一次调用已经退出,而不仅仅是可变借用范围。

我在这里错过了什么?

最佳答案

What am I missing here?

你的算法坏了。您可以通过将此调试代码添加到匹配的 Some 臂中来看到这一点:

{
    let a = current_data.borrow();
    let b = other.borrow();

    assert_ne!(a.id, b.id);
}

这失败了:

thread 'main' panicked at 'assertion failed: `(left != right)`
  left: `6`,
 right: `6`', src/main.rs:23:25

您正在尝试同时两次借用完全相同的节点

关于rust - 无法可变地借用 RefCell 内的内容,即使在前一个可变借用的范围已经结束之后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54277188/

相关文章:

regex - 在 Rust 正则表达式中模拟环视行为的最明智的方法是什么?

rust - 在 Rust 中创建一个切片并附加到它

generics - E0119 通用特征实现错误

generics - 泛化两个结构

rust - 匹配结果时的简洁日志记录

enums - 在所有枚举值中共享一个公共(public)值

windows - 来自环境变量的 Rocket 端口覆盖在 Windows 中不起作用

rust - Substrate 运行时中不同模块调用 `on_initialize` 的顺序是什么?

rust - 用 Rust 处理管道数据标准输入

rust - 结构与枚举生命周期差异