Why this code won't compile

use std::collections::HashMap;
fn main() {
    let mut map = HashMap::new();
    let v = get_or_insert(&mut map);

    println!("{:?}", v);
}

fn get_or_insert(map: &mut HashMap<u32, String>) -> &String {
   
    match map.get(&44) {
        Some(v) => v,

        None => {
            map.insert(44, "value".to_string());
            &map[&44]
        }
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
  --> src/main.rs:15:13
   |
9  | fn get_or_insert(map: &mut HashMap<u32, String>) -> &String {
   |                       - let's call the lifetime of this reference `'1`
10 |    
11 |     match map.get(&44) {
   |           --- immutable borrow occurs here
12 |         Some(v) => v,
   |                    - returning this value requires that `*map` is borrowed for `'1`
...
15 |             map.insert(44, "value".to_string());
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` due to previous error

I believe this is currently a limitation of the borrow checker. This should work, but doesn't yet.

But why wont work. pls exapand this

When in the None branch, it still thinks it has a reference to what map.get(&44) returns, even though it doesn't.

Edit: Well, that's not quite right, but I think it's close.

The reference returned from get isn't actually dropped until the end of the match expression, because it might be needed for the Some case.

One option (which you probably already considered) is splitting the check in two.

if !map.contains_key(&44) {
    map.insert(44, "value".to_string());
}
&map[&44]

This works because contains_key returns a bool, which is a 'static type, and thus no reference to map is borrowed.

1 Like

Here's the bug for this not currently working.

You should use the Entry API here. It avoids both the problem case and double-hashing.

fn get_or_insert(map: &mut HashMap<u32, String>) -> &String {
   map.entry(44).or_insert_with(|| "value".to_string())
}

Playground.

9 Likes

That bug links to the exact explanation:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.