This code compiles (playground link):
use std::collections::HashMap;
fn main() {
let mut h = HashMap::<char, Vec<i32>>::new();
h.insert('a', vec![0]);
let first_borrow = h.get_mut(&'a').unwrap();
first_borrow.push(1);
let second_borrow = h.get_mut(&'a').unwrap();
second_borrow.push(2);
}
Changing the order of the code using the borrows (the push()
calls)...
let first_borrow = h.get_mut(&'a').unwrap();
let second_borrow = h.get_mut(&'a').unwrap();
first_borrow.push(1);
second_borrow.push(2);
...makes it not compile:
error[E0499]: cannot borrow `h` as mutable more than once at a time
--> src/main.rs:8:25
|
7 | let first_borrow = h.get_mut(&'a').unwrap();
| - first mutable borrow occurs here
8 | let second_borrow = h.get_mut(&'a').unwrap();
| ^ second mutable borrow occurs here
9 | first_borrow.push(1);
| ------------ first borrow later used here
Moreover, using first_borrow
past the instantiation of second_borrow
also doesn't compile:
let first_borrow = h.get_mut(&'a').unwrap();
first_borrow.push(1);
let second_borrow = h.get_mut(&'a').unwrap();
second_borrow.push(2);
// ...
first_borrow.push(1);
This is surprising given what the documentation seems to say about scopes. In the code that compiles, why don't we have two mutable borrows there, too?
In the example that compiles, does Rust see that, after let second_borrow = ...
, there's no more mention of first_borrow
anywhere, so it unborrows the mutable borrow of first_borrow
and thus retains a single borrow across the whole scope of main()
?!
In the code that compiles, why don't we have two mutable borrows there, too?
The short answer is that two mutable borrows of the same piece of data cannot be in scope simultaneously, and the first example does not violate that restriction. Note that this is a corollary of the "one big restriction" of mutable references which is "you can have only one mutable reference to a particular piece of data in a particular scope." See the References and Borrowing section in The Rust Programming Language.
You first example compiles because
first_borrow
goes out of scope beforesecond_borrow
comes into scope. "Going out of scope" is synonymous with a variable not being referenced for the remainder of a scope. I don't know the low level details, but here is how I think of that example.For your second example that does not compile, we can see that the scopes of
first_borrow
andsecond_borrow
cross.In the example that compiles, does Rust see that, after let second_borrow = ..., there's no more mention of first_borrow anywhere, so it unborrows the mutable borrow of first_borrow and thus retains a single borrow across the whole scope of main()?!
Effectively, yes. I don't think of this as unborrowing though. As worded above, I believe the term is that
first_borrow
goes out of scope.For example, you could have written the first example like this.
And of course the second example cannot be written in such a way because the borrows cross each other.