I\'ve got a persistent compile error where Rust complains that I have an immutable borrow while I\'m trying to mutably borrow, but the immutable borrow is from another scope
This is a known issue that will be solved by non-lexical lifetimes, which is itself predicated on MIR. If it so happens that you are inserting to the same key that you are looking up, I'd encourage you to use the entry API instead.
You can add a smidgen of inefficiency to work around this for now.
HashMap
The general idea is to add a boolean that tells you if a value was present or not. This boolean does not hang on to a reference, so there is no borrow:
use std::collections::BTreeMap;
fn do_stuff(map: &mut BTreeMap<i32, i32>, key: i32) -> Option<&i32> {
if map.contains_key(&key) {
return map.get(&key);
}
map.insert(0, 0);
None
}
fn main() {
let mut map = BTreeMap::new();
do_stuff(&mut map, 42);
println!("{:?}", map)
}
Vec
Similar cases can be solved by using the index of the element instead of the reference. Like the case above, this can introduce a bit of inefficiency due to the need to check the slice bounds again.
Instead of
fn find_or_create_five<'a>(container: &'a mut Vec<u8>) -> &'a mut u8 {
match container.iter_mut().find(|e| **e == 5) {
Some(element) => element,
None => {
container.push(5);
container.last_mut().unwrap()
}
}
}
You can write:
fn find_or_create_five<'a>(container: &'a mut Vec<u8>) -> &'a mut u8 {
let idx = container.iter().position(|&e| e == 5).unwrap_or_else(|| {
container.push(5);
container.len() - 1
});
&mut container[idx]
}
These types of examples are one of the primary cases in the NLL RFC: Problem case #3: conditional control flow across functions.
Unfortunately, this specific case isn't ready as of Rust 1.34. If you opt in to the experimental -Zpolonius
feature in nightly, each of these original examples compile as-is:
use std::collections::BTreeMap;
fn do_stuff(map: &mut BTreeMap<i32, i32>, key: i32) -> Option<&i32> {
if let Some(key) = map.get(&key) {
return Some(key);
}
map.insert(0, 0);
None
}
fn find_or_create_five(container: &mut Vec<u8>) -> &mut u8 {
match container.iter_mut().find(|e| **e == 5) {
Some(element) => element,
None => {
container.push(5);
container.last_mut().unwrap()
}
}
}
See also:
How to update-or-insert on a Vec?
This is the same problem without returning the reference, which does work with the implementation of NLL available in Rust 1.32.
Double mutable borrow error in a loop happens even with NLL on
This problem but in a slightly more complicated case.
When is it necessary to circumvent Rust's borrow checker?
The ultimate escape hatch.