我在 Rust 中有一个 Graph 数据结构:
type NodeIndex = usize;
struct Graph {
nodes: Vec<NodeIndex>,
edges: Vec<(NodeIndex, NodeIndex)>,
}
我想遍历函数内的所有节点并调用一个函数,该函数使用每个节点作为元素来改变图形,例如:impl Graph {
fn mutate_fn(&mut self) {
for node in self.nodes {
self.mutate_using_node(node);
}
}
fn mutate_using_node(&mut self, node: NodeIndex) {
// mutate self here
}
}
这是行不通的,因为我会有不止一个可变引用。我也不能通过 &self,因为那样我就会有一个可变引用和一个不可变引用。这在 Rust 中是如何处理的?
最佳答案
嗯,你确实不能这样做。我可以列举两种普遍适用的主要方法,特别是你的例子
拆分借款
这种方式可能是最难和/或最慢的方式。只做借用检查器想要的:不要混淆可变和不可变借用。对于您的情况,可以像克隆 mutate_fn
中的节点一样简单:
let nodes = self.nodes.clone();
for node in nodes {
self.mutate_using_node(node);
}
没有太多细节很难推理,但我认为这是该方法的唯一方法。如果你只是改变边缘,例如这样:fn mutate_using_node(&mut self, node: NodeIndex) {
for e in &mut self.edges {
if e.0 == node {
std::mem::swap(&mut e.0, &mut e.1);
}
}
}
你可以简单地通过联合这些功能来处理它:for node in self.nodes.iter().copied() {
for e in &mut self.edges {
if e.0 == node {
std::mem::swap(&mut e.0, &mut e.1);
}
}
}
因此,总的来说,没有用于拆分代码的最终分步指南(可能复制除外)。它确实取决于代码语义。内部可变性
即
RefCell
是关于。它基本上在运行时处理借用检查规则,如果这些规则被破坏,你会感到 panic 。对于看起来像这样的情况:use std::cell::RefCell;
type NodeIndex = usize;
struct Graph {
nodes: RefCell<Vec<NodeIndex>>,
edges: RefCell<Vec<(NodeIndex, NodeIndex)>>,
}
fn mutate_fn(&self) {
for &node in self.nodes.borrow().iter() {
self.mutate_using_node(node);
}
}
fn mutate_using_node(&self, node: NodeIndex) { // <- notice immutable ref
for e in self.edges.borrow_mut().iter_mut() {
if e.0 == node {
std::mem::swap(&mut e.0, &mut e.1);
}
}
}
请记住,RefCell
不是 Sync
,因此它不能在线程之间共享。适用于带线程的 shell Mutex
或 RwLock
是一个替代方案。
关于Rust,在迭代中需要一个可变的 Self 引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62915484/