Mutable & immutable reference woes

Hi rustaceans!

In getting to know the borrow checker and HashMap design, I ran into the problem below (simplified). Suggestions for a cleaner solution than cloning, reference-counting or doing a second look-up in the map would be appreciated.

Shouldn't the compiler theoretically be able to see that both references are used in an immutable way in the call to foo(), that the "scope of mutability" has passed?

#![allow(unused)]
use std::collections::hash_map::Entry;
use std::collections::HashMap;

fn foo(map: &HashMap<i32, String>, str: &String) {}

fn main() {
    let mut map: HashMap<i32, String> = HashMap::new();

    let value: &String = match map.entry(3) {
        Entry::Occupied(oe) => oe.into_mut(),
        Entry::Vacant(ve) => ve.insert(String::from("foo")),
    };

    foo(&map, value);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `map` as immutable because it is also borrowed as mutable
  --> src/main.rs:15:9
   |
10 |     let value: &String = match map.entry(3) {
   |                                --- mutable borrow occurs here
...
15 |     foo(&map, value);
   |         ^^^^  ----- mutable borrow later used here
   |         |
   |         immutable borrow occurs here

You’re calling HashMap::entry, which takes a mutable/exclusive reference, and then “downgrading” the result to an immutable/shared reference. This would work if it were possible for this function to borrow the map mutably for a short period, and immutably for a longer period.

This would be a sensible operation, and it would be safe, but unfortunately the Rust type system doesn't have a way to express this.

I don't know of any good workarounds except to do a second hash table lookup, or to use unsafe code (in cases where you really can't afford the cost of the second lookup).

3 Likes