6

I'm learning Rust using The Rust Programming Language. I'm working on the following task in the chapter dealing with closures:

Try modifying Cacher to hold a hash map rather than a single value. The keys of the hash map will be the arg values that are passed in, and the values of the hash map will be the result of calling the closure on that key. Instead of looking at whether self.value directly has a Some or a None value, the value function will look up the arg in the hash map and return the value if it's present. If it's not present, the Cacher will call the closure and save the resulting value in the hash map associated with its arg value.

This is my solution:

use std::collections::HashMap;

struct Cacher<T>
where
    T: Fn(i32) -> i32,
{
    calculation: T,
    values: HashMap<i32, i32>,
}

impl<T> Cacher<T>
where
    T: Fn(i32) -> i32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            values: HashMap::new(),
        }
    }
    fn value(&mut self, arg: i32) -> i32 {
        match self.values.get(&arg) {
            Some(v) => *v,
            None => {
                let v = (self.calculation)(arg);
                self.values.insert(arg, v);
                v
            }
        }
    }
}

Compiler error:

error[E0502]: cannot borrow `self.values` as mutable because it is also borrowed as immutable
  --> src/main.rs:26:17
   |
22 |         match self.values.get(&arg) {
   |               ----------- immutable borrow occurs here
...
26 |                 self.values.insert(arg, v);
   |                 ^^^^^^^^^^^ mutable borrow occurs here
...
29 |         }
   |         - immutable borrow ends here

I can solve this by using:

match self.values.clone().get(&arg) { ...

Do I really need to clone the whole HashMap to use it in the match-statement and inserting a value afterwards?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
PEAR
  • 685
  • 3
  • 10
  • 20
  • 1
    `let c = &self.calculation; *self.values.entry(arg).or_insert_with(|| (c)(arg))` – Shepmaster Aug 26 '17 at 14:12
  • A more verbose way without introducng variable `c`: `use std::collections::hash_map::Entry;let v = match self.values.entry(arg) { Entry::Occupied(o) => o.into_mut(), Entry::Vacant(v) => v.insert( (self.computation)(arg) ), }; *v` – Victor Ermolaev Sep 17 '17 at 07:52

0 Answers0