自我的多个部分怎么能在这里借用?这里的 self borrowed mutably 和 immutably 不是一样的吗?

How can multiple parts of self be borrowed here? Isn't self borrowed mutably as well as immutably here?

我有这个结构:

struct PhysicsState {
    nodes: Vec<Node>,
    edges: Vec<Edge>,
}

我正在尝试理解为什么这段代码可以编译:

impl PhysicsState {
    fn remove_edge(&mut self, edge_index: usize) {
        let edge = &self.edges[edge_index];    // first borrow here
        // update the edge-index collection of the nodes connected by this edge
        for i in 0..2 {
            let node_index = edge.node_indices[i];
            self.nodes[node_index].remove_edge(edge_index);   // second (mutable) borrow here ?
        }
    }
}

虽然失败了:

impl PhysicsState {
    pub fn edge_at(&self, edge_index: usize) -> &Edge {
        &self.edges[edge_index]
    }

    pub fn node_at_mut(&mut self, node_index: usize) -> &mut Node {
        &mut self.nodes[node_index]
    }

    fn remove_edge(&mut self, edge_index: usize) {
        let edge = self.edge_at(edge_index);    // first (immutable) borrow here
        for i in 0..2 {
                let node_index = edge.node_indices[i];
                self.node_at_mut(node_index).remove_edge(edge_index);   // second (mutable) borrow here -> ERROR
            }
        }
    }
}

我原来用的是第一个版本,后来改成第二个,结果看到不行。 它失败对我来说是有道理的。 self 显然首先作为不可变借用,然后作为可变借用,正如预期的那样失败了。

我不明白的是:第一个版本是如何工作的?

很明显,第一次借用(获得 &Edge)必须在整个 for 循环中保持有效,因为它在那里使用。但是它如何设法从 self 获得对 Node 的额外可变引用呢?

第二个版本的编译器返回的错误是:error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable

为什么我在使用第一个版本时没有出现这个错误?

如果您想知道:

也许我可以改用宏来达到同样的效果,但总的来说我只想知道这里的借用是如何工作的,因为在我看来我对此有某种误解。

谢谢!

您可以在第一个版本中借用 self.edges 并随后借用 self.nodes 的原因是因为编译器理解 self.edgesself.nodes 是单独借用的内容。这也是与结构相关的所谓“Splitting Borrows”。

但是,如果您不透明地查看方法签名:

fn edge_at(&self, edge_index: usize) -> &Edge

那你看看那个,你知道借的是什么吗?并不真地。只能看到returns&Edge&self被借用了。因此 self 作为一个整体是被借用的,这不允许你进行 self.nodes 的后续可变借用,因为 self 已经被不可变地借用了。


您本质上希望发生的是调用方法允许 &self 被部分借用。这在 Rust 中不受支持。但是,有一个可追溯到 2015 年的 RFC 请求此功能。 RFC 标题为“Partial Borrowing (#1215)”,其中讨论了潜在的语法和语义。

第一个版本之所以有效,是因为在单个范围内,Rust 借用检查器可以更细粒度地“查看”借用,因此它可以判断何时从结构的不相交字段中借用引用,就像在您的示例中一样。

当你从 &self.edges[edge_index] 借用时,Rust 可以告诉不可变引用的生命周期与 self.edges 相关,而当你借用 self.nodes[node_index].remove_edge(edge_index) 时,Rust 可以告诉可变引用的生命周期与 self.edges 相关self.nodes 并且由于 self.edgesself.nodes 是不相交的结构字段,因此引用之间没有重叠,它们可以同时存在。

第二个例子失败了,因为你将单个作用域分解成多个作用域,现在 Rust 借用检查器只能根据你通过方法签名通知它的内容来推断引用的生命周期,以及第一个方法签名 fn edge_at(&self, edge_index: usize) -> &Edge 表示返回的引用的生命周期与 &self 相关,第二个方法签名 fn node_at_mut(&mut self, node_index: usize) -> &mut Node 表示返回的引用的生命周期与 &mut self 相关,现在当您在一个单一作用域 Rust 认为你有冲突的重叠引用,它们都是从 self 借用的(而不是像以前那样从 self.edgesself.nodes 借用的非重叠引用)所以它会抛出一个编译错误。