0

How do I use an HashMap as a cache correctly? Accessing the value needs to borrow the HashMap (as mutable in my case) which restricts my ability to access more than one value, and restrict the usage of one value at the same time (and the cache itself).

Hypothetical example below:

use std::collections::HashMap;

struct Cache {
    storage: HashMap<i32, String>,
}

impl Cache {
    fn new() -> Self {
        Cache {
            storage: HashMap::new(),
        }
    }

    fn get_or_compute(&mut self, x: i32) -> &mut String {
        self.storage.entry(x).or_insert(x.to_string())
    }
}

fn do_something(cache: &mut Cache, s: &String) {
    println!("Cache value:{}, s:{}", cache.get_or_compute(23), s)
}

fn main() {
    let mut cache = Cache::new();
    let a = cache.get_or_compute(1);
    let _b = cache.get_or_compute(2);

    do_something(&mut cache, a)
}

Rust Playground

The compiler (rightfully) complains with:

error[E0499]: cannot borrow `cache` as mutable more than once at a time
  --> src/main.rs:30:14
   |
29 |     let a = cache.get_or_compute(1);
   |             ----- first mutable borrow occurs here
30 |     let _b = cache.get_or_compute(2);
   |              ^^^^^ second mutable borrow occurs here
31 | 
32 |     do_something(&mut cache, a)
   |                              - first borrow later used here

error[E0499]: cannot borrow `cache` as mutable more than once at a time
  --> src/main.rs:32:18
   |
29 |     let a = cache.get_or_compute(1);
   |             ----- first mutable borrow occurs here
...
32 |     do_something(&mut cache, a)
   |                  ^^^^^^^^^^  - first borrow later used here
   |                  |
   |                  second mutable borrow occurs here

I suppose it has to do with some missing lifetime annotation, but I'm too noob to see how to fix the issue.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Steve Gury
  • 15,158
  • 6
  • 38
  • 42
  • 1
    Your code is rejected by the compiler because it would introduce memory unsafety. The second call to `get_or_compute` could easily add a new value to the `HashMap`, causing it to reallocate, which would invalidate the memory address stored in `a` — this code cannot be safe. If you had written this in C or C++, you would have introduced a *very* hard to find bug. – Shepmaster Feb 14 '19 at 01:35
  • You can either move the second get after you drop "a" or check out the interior mutability pattern using refcell to wrap the map – Gurwinder Singh Feb 14 '19 at 01:37

0 Answers0