【发布时间】: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<RefCell<Node>>(尽管Node本身不再需要包含上面示例中显示的字段)。 -
@eggyal
RwLock和Mutex还提供了更高级别的运行时借用检查。这些类型是使用UnsafeCell实现的,RefCell也是如此——当谈到 rust 的内部可变性时,all roads point toUnsafeCell。 -
为了阐明
num_before字段的用途,这段代码来自tsort 实用程序的Rust 版本。通过为从输入读取的每对标记调用add_order来创建标记的部分排序。对于每个标记,num_before字段基本上是在部分排序中已知在它之前的标记数量的计数,这对于生成tsort输出很有用。
标签: rust