Why doesn't the following code compile (playground):
use std::collections::HashMap;
fn main() {
let mut h: HashMap<u32, u32> = HashMap::new();
h.insert(0, 0);
h.insert(1, h.remove(&0).unwrap());
}
The borrow checker complains that:
error[E0499]: cannot borrow `h` as mutable more than once at a time
--> src/main.rs:6:17
|
6 | h.insert(1, h.remove(&0).unwrap());
| - ------ ^ second mutable borrow occurs here
| | |
| | first borrow later used by call
| first mutable borrow occurs here
The code is safe, however, and an almost mechanical transformation of the last line makes it compile (playground):
//h.insert(1, h.remove(&0).unwrap());
let x = h.remove(&0).unwrap();
h.insert(1, x);
It was my understanding that this kind of issue got resolved with non-lexical lifetimes. This question is an example, and there are many others.
Is there some subtlety that makes the first variant incorrect after all, so Rust is correct to refuse it? Or is the NLL feature still not finished in all cases?
Your question also applies for a related case that may be more surprising — having the method call require &self
when the method argument requires &mut self
:
use std::collections::HashMap;
fn main() {
let mut h: HashMap<u32, u32> = HashMap::new();
h.insert(0, 0);
h.contains_key(&h.remove(&0).unwrap());
}
The Rust borrow checker uses what it calls two-phase borrows. An edited transcription of a chat I had with Niko Matsakis:
The idea of two-phase borrows is that the outer
&mut
is treated like an&
borrow until it is actually used, more or less. This makes it compatible with an inner&
because two&
mix, but it is not compatible with an inner&mut
.If we wanted to support that, we'd have had to add a new kind of borrow -- i.e., an "unactivated"
&mut
wouldn't act like an&
, it would act like something else (&const
, maybe... "somebody else can mutate")It's less clear that this is OK and it seemed to add more concepts so we opted not to support it.
As you stated, this is safe because the inner borrow is completed before the outer borrow starts, but actually recognizing that in the compiler is overly complex at this point in time.
See also:
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