I created a helper function get_node()
for a common pattern, but for some reason it leads to borrow errors, but when copy pasting its contents it does not. I've marked the problematic lines with comments:
type NodeMap = HashMap<i32, Node>;
pub trait IdGenerator {
fn gen(&mut self) -> i32;
}
pub struct Tree<'a> {
active: i32,
nodes: NodeMap,
generator: &'a mut dyn IdGenerator,
}
impl<'a> Tree<'a> {
pub fn create_sibling(&mut self) {
let active = self.nodes.get(&self.active).unwrap(); // <-- THIS WORKS
// let active = self.get_node(self.active); // <-- THIS FAILS
let mut node = Node::new(self.generator.gen(), active.parent);
// [......]
}
fn get_node(&self, id: i32) -> &Node {
self.nodes.get(&id).unwrap()
}
}
Uncommenting and commenting the two let active...
lines leads to the following error:
error[E0502]: cannot borrow `*self.generator` as mutable because it is also borrowed as immutable
--> src/tree_new.rs:35:34
|
34 | let active = self.get_node(self.active);
| ---- immutable borrow occurs here
35 | let mut node = Node::new(self.generator.gen(), active.parent);
| ^^^^^^^^^^^^^^^^^^^^ ------------- immutable borrow later used here
| |
| mutable borrow occurs here
The error messages makes sense, but what I'm confused on is why the same borrowing logic doesn't apply to the original code without the helper function.
This line immutably borrows
self.nodes
for the lifetime ofactive
(which is the current block scope):Later in the same scope you mutably borrow
self.generator
butself.nodes
andself.generator
don't overlap so there's no problems.However, this line immutably borrows all of
self
, which includesself.generator
, for the lifetime ofactive
(which is the same block scope as before):Now since all of
self
is immutably borrowed, when you try to mutably borrowself.generator
later, the immutable borrow ofself
and the mutable borrow ofself.generator
overlap and you get a compiler error.Unfortunately this is a known limitation of Rust. There's no way to "enrich" the type signature of your helper function to communicate to the Rust compiler that the immutable borrow depends specifically only on
self.nodes
and not on all ofself
. However there are people who are interested in adding this to the language and you can track their progress in the Partial Borrowing issue on the Rust RFCs repo. The typical workaround is to split borrows but your helper function is so tiny I don't think that approach applies here and your best bet would be to just get rid of the helper function and continue writing the logic inline.