How can I borrow from a HashMap to read and write at the same time?

Solution 1:

TL;DR: You will need to change the type of HashMap


When using a method, the compiler does not inspect the interior of a method, or perform any runtime simulation: it only bases its ownership/borrow-checking analysis on the signature of the method.

In your case, this means that:

  • using get will borrow the entire HashMap for as long as the reference lives,
  • using get_mut will mutably borrow the entire HashMap for as long as the reference lives.

And therefore, it is not possible with a HashMap<K, V> to obtain both a &V and &mut V at the same time.


The work-around, therefore, is to avoid the need for a &mut V entirely.

This can be accomplished by using Cell or RefCell:

  • Turn your HashMap into HashMap<K, RefCell<V>>,
  • Use get in both cases,
  • Use borrow() to get a reference and borrow_mut() to get a mutable reference.
use std::{cell::RefCell, collections::HashMap};

fn main() {
    let mut map = HashMap::new();

    map.insert("1", RefCell::new(1));
    map.insert("2", RefCell::new(2));

    {
        let a = map.get("1").unwrap();
        println!("a: {}", a.borrow());

        let b = map.get("2").unwrap();
        println!("b: {}", b.borrow());
        *b.borrow_mut() = 5;
    }

    println!("Results: {:?}", map);
}

This will add a runtime check each time you call borrow() or borrow_mut(), and will panic if you ever attempt to use them incorrectly (if the two keys are equal, unlike your expectations).


As for using fields: this works because the compiler can reason about borrowing status on a per-field basis.

Solution 2:

Something appears to have changed since the question was asked. In Rust 1.38.0 (possibly earlier), the following compiles and works:

use std::collections::HashMap;

fn f(a: &i32, b: &mut i32) {}

fn main() {
    let mut map = HashMap::new();

    map.insert("1", 1);
    map.insert("2", 2);

    let a: &i32 = map.get("1").unwrap();
    println!("a: {}", a);

    let b: &mut i32 = map.get_mut("2").unwrap();
    println!("b: {}", b);
    *b = 5;

    println!("Results: {:?}", map)
}

playground

There is no need for RefCell, nor is there even a need for the inner scope.