1

Consider the following attempt to make a function memoizer (playground link):

use std::collections::HashMap;

pub struct Cacher<F, IN, OUT> {
    function: F,
    cache: HashMap<IN, OUT>,
}

impl<F, IN, OUT> Cacher<F, IN, OUT>
where
    F: Fn(IN) -> OUT,
    IN: std::cmp::Eq + std::hash::Hash + std::marker::Copy,
    OUT: std::marker::Copy,
{
    fn new(function: F) -> Cacher<F, IN, OUT> {
        Cacher {
            function: function,
            cache: HashMap::new(),
        }
    }

    fn at(&mut self, arg: IN) -> &OUT {
        match self.cache.get(&arg) {
            None => {
                let val = (self.function)(arg);
                self.cache.insert(arg, val);
                &val // FAIL!
            }
            Some(val) => val,
        }
    }
}

The at method fails to compile:

error[E0597]: `val` does not live long enough
  --> src/lib.rs:26:18
   |
26 |                 &val // FAIL!
   |                  ^^^ borrowed value does not live long enough
27 |             }
   |             - borrowed value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 21:5...
  --> src/lib.rs:21:5
   |
21 | /     fn at(&mut self, arg: IN) -> &OUT {
22 | |         match self.cache.get(&arg) {
23 | |             None => {
24 | |                 let val = (self.function)(arg);
...  |
29 | |         }
30 | |     }
   | |_____^

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

My understanding is that in the case that the value must be computed, the returned &val isn't known to live long enough. I tried a few things to explain to the compiler that the returned &val should be expected to live as long as the Cacher that contains it, but so far I have not succeeded.

How do we give the compiler the required information about &val's lifetime?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
DanielSank
  • 3,303
  • 3
  • 24
  • 42
  • 1
    This has been asked before, but I can't find the duplicate right now. Essentially, you need to use `self.cache.entry(arg).or_insert_with(|| function(arg))`, with `let function = &self.function`. (You can't use `self.function` inside the closure since this would borrow `self`. [Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=39e0efdda221d45c1520c5c4180f832b)) – Sven Marnach Nov 28 '18 at 19:37
  • @SvenMarnach FWIW, I found via `site:stackoverflow.com rust disjoint borrow` – Shepmaster Nov 28 '18 at 19:52

0 Answers0