Is the rust compiler being too conservative by preventing a "simultaneous" borrow and mutable borrow in two separate branches of the function?
What is the correct way to do this?
use std::collections::HashMap;
use core::hash::Hash;
fn f<K: Eq + Hash, V>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
if let Some(result) = map.entry(key).or_default() {
return result
} else {
map.insert(key, Some(default));
return &default
}
}
Errors:
error[E0499]: cannot borrow `*map` as mutable more than once at a time
--> src/lib.rs:8:9
|
4 | fn f<K: Eq + Hash, V>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
| - let's call the lifetime of this reference `'1`
5 | if let Some(result) = map.entry(key).or_default() {
| -------------- first mutable borrow occurs here
6 | return result
| ------ returning this value requires that `*map` is borrowed for `'1`
7 | } else {
8 | map.insert(key, Some(default));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
error[E0382]: use of moved value: `key`
--> src/lib.rs:8:20
|
4 | fn f<K: Eq + Hash, V>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
| --- move occurs because `key` has type `K`, which does not implement the `Copy` trait
5 | if let Some(result) = map.entry(key).or_default() {
| --- value moved here
...
8 | map.insert(key, Some(default));
| ^^^ value used here after move
|
help: consider further restricting this bound
|
4 | fn f<K: Eq + Hash + std::marker::Copy, V>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
| +++++++++++++++++++
error[E0515]: cannot return reference to function parameter `default`
--> src/lib.rs:9:16
|
9 | return &default
| ^^^^^^^^ returns a reference to data owned by the current function
error[E0382]: borrow of moved value: `default`
--> src/lib.rs:9:16
|
4 | fn f<K: Eq + Hash, V>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
| ------- move occurs because `default` has type `V`, which does not implement the `Copy` trait
...
8 | map.insert(key, Some(default));
| ------- value moved here
9 | return &default
| ^^^^^^^^ value borrowed here after move
|
help: consider restricting type parameter `V`
|
4 | fn f<K: Eq + Hash, V: std::marker::Copy>(map: &mut HashMap<K, Option<V>>, key: K, default: V) -> &V {
| +++++++++++++++++++
I know that this can be done more elegantly via the entry api. The real example isn't a hashmap. I've made a contrived example that uses only the following operations:
- get a value option from a key
- insert an key, value