Consider the following Rust program
struct Foo {
bar: Vec<i32>,
baz: i32
}
impl Foo {
fn get_an_iter(&self) -> impl Iterator<Item=&i32> {
self.bar.iter()
}
fn do_mut_stuff_works(&mut self) {
for num in self.bar.iter() {
self.baz += num;
}
}
fn do_mut_stuff_does_not_work(&mut self) {
for num in self.get_an_iter() {
self.baz += num;
}
}
}
In do_mut_stuff_works
I am iterating over the vector bar
and change the field baz
. To do this, the method needs to take &mut self
.
Now if I move the creation of the iterator from inline to a method get_an_iter
, and try to iterate over that in do_mut_stuff_does_not_work
, the borrow checker complains.
How is it that, when using get_an_iter
, the immutable borrow stays alive for the whole loop such that my mutable borrow at self.baz += num
fails, but when I just create the iterator inside the method, with for num in self.bar.iter()
, the borrow checker is happy?
The full compiler output is:
Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `self.baz` as mutable because it is also borrowed as immutable
--> src/lib.rs:20:13
|
19 | for num in self.get_an_iter() {
| ------------------
| |
| immutable borrow occurs here
| immutable borrow later used here
20 | self.baz += num;
| ^^^^^^^^^^^^^^^ mutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` (lib) due to 1 previous error
Rust can split borrows among fields. In do_mut_stuff_works
, you start with a mutable borrow of self
. When you take self.bar.iter()
, that borrow is split: self.bar
is immutably borrowed for the duration of the for
loop. During this time, the mutable borrow of self
as a whole is invalid, but the mutable borrow of self.baz
remains. Thus you can increment self.baz
in the body.
In do_mut_stuff_does_not_work
, you make an immutable borrow to the whole of self
and give it to get_an_iter
, so that borrow of self
lasts for the whole for
loop. Rust does not look into the definitions of functions to split borrows; that would be leaking implementation details about the function that aren't in the signature. So even though get_an_iter
only needs an immutable borrow of self.bar
, do_mut_stuff_does_not_work
only knows that it has lent the entirety of self
, immutably, to get_an_iter
. Thus there is no mutable borrow of self.baz
in the body of the loop that would justify incrementing it.