In Rust, the compiler stops at the function call boundary when evaluating generic parameters, which includes generic lifetime parameters. In your case 1, you are calling a method:
fn get_qux(&mut self) -> &mut Qux {
&mut self.qux
}
This function indicates that all of self
will be borrowed mutably, and that the returned reference will live as long as self
will. During this time, no other borrows (mutable or not) of self or it's components may be made.
In your second case, you make up a completely new Qux
that has no attachment to your struct whatsoever. It's not a really great example, because it has very different meaning. If this case works for you, you should do that. However, you won't be modifying the same thing as case 1.
In the third case, you avoid the function call. That means that the compiler has a bit more information about what exactly is borrowed. Specifically, it can see that self.qux
doesn't interact at all with self.bars
, so there is no error.
You can make your original example work by adding a new scope:
fn run(&mut self) {
{
let mut qux = self.get_qux();
let qux_mut = &mut qux;
qux_mut.baz = true;
}
for bar in &self.bars {
println!("{:?}", bar);
}
}
Here, the artificial scope clearly defines where the mutable borrow ends. Once the borrow is over, other items are allowed to make new borrows.
If you need to modify qux
inside the loop, then you are required to follow the third pattern:
let mut qux = &mut self.qux;
for bar in &self.bars {
qux.baz = ! qux.baz;
println!("{:?}", bar);
}
Or the simpler:
for bar in &self.bars {
self.qux.baz = ! self.qux.baz;
println!("{:?}", bar);
}
Many times, you can refactor your code to create new structs that have information and encapsulate a nice mutation boundary to make code like this.