My aim is to write a function
fn get_mut_or_insert_result<K, V, F>(
map: &mut BTreeMap<K, V>,
key: K,
or_insert: F,
) -> Result<&mut V>
where
K: Ord,
F: FnOnce() -> Result<V>,
which will do exactly one of:
- when the key exists, return the mutable reference to the existing value without calling
or_insert
. - when the key is missing, insert the
or_insert
return (ifOk
) into the map and return a mutable reference to it. - return
Err
ifor_insert
is called and returnsErr
.
In this topic, the same question was asked, guessing a manual match was the needed workaround. The replies reached the same conclusion. I did too in my independent attempt. But the topic did not show the workaround. When trying to implement it myself, I got bitten by some conservative borrow checking, and I didn't like my final solution.
Here was my first attempt:
// (function types omitted, see first snippet)
fn get_mut_or_insert_result(map, key, or_insert) {
if let Some(value) = map.get_mut(&key) {
Ok(value)
} else {
let value = or_insert()?;
// ERROR: second mutable borrow of map
Ok(map.entry(key).or_insert(value))
}
}
The compiler says map
is being borrowed more than once at a time. This error remains even if a return
is used in the first branch instead of else
. I see that it's being conservative about what get_mut
does with its mutable borrow, but even then I don't get why a returned None
would be live in the else block.
My second attempt did work:
// (function types omitted, see first snippet)
fn get_mut_or_insert_result(map, key, or_insert) {
let mut e = map.entry(key);
match e {
std::collections::btree_map::Entry::Vacant(_) => {
let value = or_insert()?;
Ok(e.or_insert(value))
},
std::collections::btree_map::Entry::Occupied(_) => {
Ok(e.or_insert_with(|| unreachable!()))
},
}
}
That unreachable
is ugly though, and or_insert_with
is no help for understanding intent.
Trying to use the value inside Occupied
, like its get_mut
method, always ran into errors about borrowing from temporaries.
In total, the questions raised:
- What's the best way to implement this function?
- Why did the borrow checker fail the first attempt?
- Why doesn't
OccupiedEntry::get_mut
return&'a mut V
?