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?