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:
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);
}
fn main() {}
This is because the compiler is smarter and can see that you aren't using world
anymore by the time you get to map.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:
let mut thing = map.remove(&5);
{
let mut world = map.get_mut(&4);
}
let mut hello = map.get_mut(&1);
Why does
HashMap::get_mut()
take ownership of the map
It absolutely does not do that. Ownership is a precise term in Rust code. Note that the error message specifically says
previous borrow of
map
occurs here
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:
fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Hash + Eq,
In words, this could be read as
Given a mutable reference to a
HashMap
(&mut self
) and something that can be used to find the key (K: Borrow<Q>, Q: Hash + Eq
), return a mutable reference to the value if one matches (Option<&mut V>
)
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
:
fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq,
This returns an owned value, not a reference into the HashMap
. Once the method is done, the borrow of the map is over.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With