I have the following code that inserts some values into a HashMap and then gets them back out:
use std::collections::HashMap;
fn things() {
let mut map = HashMap::new();
map.insert(5, "thing");
map.insert(4, "world");
map.insert(1, "hello");
let mut thing = map.remove(&5);
let mut world = map.get_mut(&4);
let mut hello = map.get_mut(&1);
}
Attempting to compile this code gives the following error:
error[E0499]: cannot borrow `map` as mutable more than once at a time
--> src/main.rs:10:21
|
9 | let mut world = map.get_mut(&4);
| --- first mutable borrow occurs here
10 | let mut hello = map.get_mut(&1);
| ^^^ second mutable borrow occurs here
11 | }
| - first borrow ends here
After perusing the API docs for both the remove()
and get_mut()
methods (fortunately they are pretty close to each other!) there is nothing that stands out to me from the method signatures why the remove()
method does not mutably borrow the map for the rest of the current scope while the get_mut()
method does.
The other piece of data that I have that also mystifies me is that this code compiles:
use std::collections::HashMap;
fn things() {
let mut map = HashMap::new();
map.insert(5, "thing");
map.insert(4, "world");
map.insert(1, "hello");
let mut thing = map.remove(&5);
map.get_mut(&4);
let mut hello = map.get_mut(&1);
}
Not storing the result of the first call to get_mut()
doesn't cause the map to be mutably borrowed for the rest of the scope? How could I have known this from looking at the documentation? Am I missing something else?
This error is a limitation of the implementation of the borrow checker before non-lexical lifetimes. With those enabled, the original code will work as-is:
This is because the compiler is smarter and can see that you aren't using
world
anymore by the time you get tomap.get_mut(&1)
, so it doesn't need to have a valid reference anymore.You can get equivalent code in previous versions of Rust by adding an explicit scope:
It absolutely does not do that. Ownership is a precise term in Rust code. Note that the error message specifically says
A borrow is not ownership. If I borrow your car, I don't own your car.
Your real question is "why does it borrow it for the rest of the scope". Let's look at the signature:
In words, this could be read as
However, that returned mutable reference will be changing something in the
HashMap
, that's why it's a mutable reference at all. You are only allowed to have multiple immutable borrows OR one mutable borrow at a time. This prevents writing code that causes inconsistencies and safety issues.Let's look at
remove
:This returns an owned value, not a reference into the
HashMap
. Once the method is done, the borrow of the map is over.