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 ofself. When you takeself.bar.iter(), that borrow is split:self.baris immutably borrowed for the duration of theforloop. During this time, the mutable borrow ofselfas a whole is invalid, but the mutable borrow ofself.bazremains. Thus you can incrementself.bazin the body.In
do_mut_stuff_does_not_work, you make an immutable borrow to the whole ofselfand give it toget_an_iter, so that borrow ofselflasts for the wholeforloop. 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 thoughget_an_iteronly needs an immutable borrow ofself.bar,do_mut_stuff_does_not_workonly knows that it has lent the entirety ofself, immutably, toget_an_iter. Thus there is no mutable borrow ofself.bazin the body of the loop that would justify incrementing it.