【问题标题】:Rust multiple mutable self borrowing from method callsRust 从方法调用中借用多个可变自
【发布时间】:2020-07-22 21:21:22
【问题描述】:

我正在学习 Rust。对于我的第一个程序,我编写了以下代码来维护有关偏序的数据:

use std::collections::{HashMap, HashSet};

struct Node {
    num_before: usize,
    successors: HashSet<String>,
}

impl Node {
    fn new() -> Node {
        Node {
            num_before: 0,
            successors: HashSet::new(),
        }
    }
}

pub struct PartialOrdering {
    node: HashMap<String, Node>,
}

impl PartialOrdering {
    pub fn new() -> PartialOrdering {
        PartialOrdering {
            node: HashMap::new(),
        }
    }

    pub fn get_node(&mut self, name: &String) -> &mut Node {
        self.node.entry(name.clone()).or_insert_with(Node::new)
    }

    pub fn add_order(&mut self, before: &String, after: &String) {
        let mut before_node = self.get_node(before);
        if after != before {
            let mut after_node = self.get_node(after);
            if before_node.successors.insert(after.clone()) {
                after_node.num_before += 1;
            }
        }
    }
}

编译此代码会产生此错误:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> main.rs:35:25
   |
33 |         let before_node = self.get_node(before);
   |                           ---- first mutable borrow occurs here
34 |         if after != before {
35 |             let mut after_node = self.get_node(after);
   |                                  ^^^^ second mutable borrow occurs here
36 |             if before_node.successors.insert(after.clone()) {
   |                ---------------------- first borrow later used here

诚然,我是 Rust 借用规则的新手,但这个问题让我很困惑。请告诉我我做错了什么,我该如何解决?

【问题讨论】:

  • 你的节点持有对它们的“后继者”的引用,以及它们的“前驱者”的计数(因此有多少对它们的引用——大概是为了在前驱者仍然持有引用它?),令人震惊地让人想起标准库的引用计数指针Rc。使用它可以让您获得节点指针的廉价副本的所有权,因此不会持有可变借用。
  • 但是,改变Rc 指向的数据需要“内部可变性”(即运行时,而不是编译时,借用检查),这可以是使用RefCell 实现。放在一起,您将拥有Rc&lt;RefCell&lt;Node&gt;&gt;(尽管Node 本身不再需要包含上面示例中显示的字段)。
  • @eggyal RwLockMutex 还提供了更高级别的运行时借用检查。这些类型是使用UnsafeCell 实现的,RefCell 也是如此——当谈到 rust 的内部可变性时,all roads point to UnsafeCell
  • 为了阐明num_before 字段的用途,这段代码来自tsort 实用程序的Rust 版本。通过为从输入读取的每对标记调用 add_order 来创建标记的部分排序。对于每个标记,num_before 字段基本上是在部分排序中已知在它之前的标记数量的计数,这对于生成tsort 输出很有用。

标签: rust


【解决方案1】:

问题在于,在 Rust 中,禁止一次对一个对象使用多个可变引用 (&amp;mut)(有关详细信息,请参阅 here)。您的get_node() 接受&amp;mut self 并使用它来获取包含在self 中的&amp;mut Node(其中selfPartialOrdering)。只要get_node() 返回的值存在,这就会导致self 的可变借用存在,从而防止对get_node() 的其他调用。这意味着您不能在同一范围内拥有 before_node: &amp;mut Nodeafter_node: &amp;mut Node,很遗憾。

【讨论】:

  • 感谢您清晰及时的回复。我修改了代码,使before_nodeafter_node 在不同的范围内,一切正常。 真的让我印象深刻的是,Rust 编译器检测到 get_node 返回值是对 self 的一部分的引用,因此算作相互引用。
  • 很高兴帮助@FrankLhota!如果您想更好地了解借用检查器在这里所​​做的事情,您可能需要阅读lifetimes(更高级的东西here)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多