3

I've just started learning Rust and have come across a seemingly bizarre behaviour of HashMap's entry() method. In the below example, the method takes a mutable reference and returns the Entry enum. I am not even capturing and persisting the returned value. But the borrow checker seems to think a mutable reference to "s" is still in scope when the next iteration begins.

let mut window: HashMap<&String, i32> = HashMap::new();
let mut s = "help_me".to_string();
loop {
    let p = &mut s;        // ERROR
    window.entry(p);
}

This shows snippet panics with error:

Line 27, Char 26: cannot borrow `s` as mutable more than once at a time (solution.rs)
   |
27 |             window.entry(&mut s);
   |                          ^^^^^^ `s` was mutably borrowed here in the previous iteration of the loop

Can someone please explain this behaviour to me?

cafce25
  • 15,907
  • 4
  • 25
  • 31
  • Modified the snippet to use a temporary variable to show that if the entry() call is removed, it compiles fine as it should. Does this mean the entry() method is doing something stateful? – Naveen Santhanavel Dec 19 '22 at 15:33

1 Answers1

6

The map's key type is &String. Let's call the lifetime &'a String.

The entry() method may need insert the key into the map (this is the entire idea of the entry API). Therefore, it takes the same type as the key, that is, &'a String.

As you borrow s, you need the borrow to last for 'a so you can get &'a String from it. So, you borrow it mutably for 'a.

But the next time the loop is executing, while 'a is still active (because the map is still alive), you borrow s again, again for 'a, while it is already mutably borrowed for 'a so you cannot borrow it during that time. This is the error.

You probably want a map of String (not &String), or, even if you do want references, you can instead of doing window.entry(&mut s); to do window.entry(&s); and then it will work because you can borrow a value for a shared reference multiple times.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • Thanks for your explanation. Does that mean the given key (&String) would be in scope till the map is alive? If so, that would mean I cannot mutable borrow the key until the map dies. This seems strange since the entry method did not result in the key being stored in the map. – Naveen Santhanavel Dec 19 '22 at 15:39
  • 1
    @NaveenSanthanavel Yes. Despite `entry()` itself not storing the key in the map, future methods on the returned `Entry` may (e.g. `or_insert()`). Thus, it needs to act as if it inserts it into the map. – Chayim Friedman Dec 19 '22 at 15:40
  • That makes sense. But when the returned enum goes out of scope, this is no longer true though. It would've been more intuitive for the passed key also to go out of scope when the returned enum does. – Naveen Santhanavel Dec 19 '22 at 15:44
  • 2
    @NaveenSanthanavel But when the returned enum goes out of scope, the key may have already been inserted into the map. The borrow checker cannot track that. – Chayim Friedman Dec 19 '22 at 15:45
  • Gotcha! Thank you for explaining. I guess the takeaway for me is to avoid references for keys in HashMap :) – Naveen Santhanavel Dec 19 '22 at 15:46