I have this struct:
struct PhysicsState {
nodes: Vec<Node>,
edges: Vec<Edge>,
}
and I'm trying to understand why this code compiles:
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 ?
}
}
}
while this fails:
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
}
}
}
}
I originally used the first version and later changed it to the second, only to see it fail.
And it makes sense to me that it fails. self
is clearly borrowed as immutable first and then as mutable, which fails, as expected.
What I don't understand is: How does the first version work?
Clearly the first borrow (getting the &Edge
) has to stay alive throughout the for loop, since it is used there. But how does it manage to get an additional mutable reference to a Node
from self
then?
The error returned by the compiler for the second version is: error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
Why don't I get this error when using the first version?
In case you wonder:
Perhaps I could instead use a macro to achieve the same effect, but over all I just wanna know how the borrowing works here, because it seems to me that I have some kind of misunderstanding about it.
Thanks!
The reason you can borrow self.edges
and subsequently self.nodes
in your first version, is because the compiler understands that self.edges
and self.nodes
are individually what's being borrowed. This is also what's called "Splitting Borrows" in relation to structs.
However, if you opaquely look at the method signature:
fn edge_at(&self, edge_index: usize) -> &Edge
Then by looking at that, do you know what's being borrowed? Not really. All you can see is that it returns &Edge
and &self
is being borrowed. Thereby self
as a whole is what's being borrowed, which disallows you doing the subsequent mutable borrow of self.nodes
, because self
is already immutably borrowed.
What you essentially desire occurring, is that calling methods allow &self
to be partially borrowed. This is not supported in Rust. However, there is an RFC dating back to 2015 requesting this feature. The RFC is titled "Partial Borrowing (#1215)", which discusses potential syntax and semantics.