1

I'm solving a task from the Rust Book:

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

impl<T> Cacher<T>
where
    T: Fn(i32) -> i32,
{
    // ...
    fn value(&mut self, arg: i32) -> i32 {
        let v = self.hm.get(&arg); // borrowing returned Option<&V>
        match v {
            // matching owned value
            Some(v) => *v, //return Copy'ied i32
            None => {
                let v2 = (self.calculation)(arg); // get result of closure
                self.hm.insert(arg, v2); // memoize gotten value
                v2 // return value
            }
        }
    }
    // ...
}

However, the compiler gives the following:

let v = self.hm.get(&arg);
--- immutable borrow occures here

Ok, I understand it, but the next message:

self.hm.insert(arg, v2);
^^^^^^^ mutable borrow occures here

How does this happens if I don't change the borrowed (v) value in self.hm.insert(arg, v2);?

I did a mutable borrow by changing let v to let mut v, but it didn't help: the compiler reports the same error.

How could I change my code to be able to insert a memoized value into the hash map?

Sorry for vague title, didn't find better description.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Bulat M.
  • 680
  • 9
  • 25
  • 3
    See also [How do I remove excessive `clone` calls from a struct that caches arbitrary results?](https://stackoverflow.com/q/46388977/155423) – Shepmaster Nov 21 '17 at 20:32
  • 2
    `let calc = &self.calculation; *self.hm.entry(arg).or_insert_with(|| calc(arg))` – Shepmaster Nov 21 '17 at 20:36
  • @Shepmaster, I don't know how to apply V::clone in my code, because I return i32, not generic value. if let Some(v) = self.values.get(&arg).map(V::clone) { – Bulat M. Nov 21 '17 at 20:46
  • 1
    I'm not saying that the `Clone` solution solves your current problem — that's what the duplicate and my second comment are for. The `Clone` solution is attempting to answer your next question when you try to make it even more generic. – Shepmaster Nov 21 '17 at 20:50
  • Could you please show how to implement that method? I am reading book sequentially and haven't already acquired knowledge to comprehend syntax you used, for example *self... – Bulat M. Nov 21 '17 at 20:59
  • 2
    That *is* the implementation of the method, I've already shown it to you: `fn value(&mut self, arg: i32) -> i32 { let calc = &self.calculation; *self.hm.entry(arg).or_insert_with(|| calc(arg)) }`. `*` is the dereference operator, you used it in your own code: `Some(v) => *v`. – Shepmaster Nov 21 '17 at 21:01
  • @BulatM. To clarify on _why you get the original error_, it's because `v` is (possibly) still a reference to some memory allocated by the HashMap. Rust avoids magic, and thus doesn't "know" that `v` is empty when you enter the `None` branch. Inside `None=>`, you insert into the HashMap, which could cause a re-allocation - and this would invalidate `v` if it held a reference to memory that was just re-allocated. The solution is the same as the "duplicate" question - to use the `entry` api. – daboross Nov 21 '17 at 22:14
  • 1
    @daboross FWIW, this *is* a weakness in the borrow checker. The work for non-lexical lifetimes should make the naïve hashmap case work (although still be less efficient than the entry API). – Shepmaster Nov 22 '17 at 01:28
  • Sorry for offtopic, but: @Shepmaster, how did you get such a solid knowledge and thorough understanding of details? In contrast, yet I can hardly get my Rust code compilable( – Bulat M. Nov 22 '17 at 04:44
  • 1
    @BulatM. that part is simple — I've been reading / writing / editing Rust questions and answers for over three years at this point. Add to that general programming in higher and lower level languages for ~10 years. Don't give up on Rust though; in many cases the compiler protects you from shooting yourself in the foot with regards to safety (this case isn't really one of them, sadly). Most of the times the compiler stops you is for a good reason, and once satisfied your code will be very likely to do what you told it to! – Shepmaster Nov 22 '17 at 13:38

0 Answers0