Suppose there is a type Gen
which creates values of type Item
:
pub struct Item<'a> { pub link: &'a u8 }
// A generator of `Item`s.
pub struct Gen { root: u8 }
impl Gen {
pub fn gen(&mut self) -> Item {
Item { link: &self.root }
}
}
As can be seen Item
s created by Gen
retain a reference to Gen::root
.
Assume that Gen::gen
is an expensive operation and we would like to cache Item
s instead of always creating them again:
use std::collections::HashMap;
use std::collections::hash_map::Entry;
// A cache has a generator and caches `Item`s.
pub struct Cache<'a> {
gen: Gen,
cache: HashMap<String, Item<'a>>
}
impl<'a> Cache<'a> {
pub fn new() -> Cache<'a> {
Cache {
gen: Gen { root: 42 },
cache: HashMap::new()
}
}
pub fn gen(&'a mut self) -> &'a Item {
match self.cache.entry(String::from("some-lookup-key")) {
Entry::Occupied(x) => x.into_mut(),
Entry::Vacant(x) => {
let i = self.gen.gen();
x.insert(i)
}
}
}
}
This works as expected:
#[test]
fn test_single() {
let mut c = Cache::new();
assert_eq!(42, *c.gen().link);
}
However it seems that Cache
can not be used from different threads:
#[test]
fn test_multi() {
use std::sync::{Arc, Mutex};
use std::thread;
let a = Arc::new(Mutex::new(Cache::new()));
let t = thread::spawn(move || {
let mut c = a.lock().unwrap();
assert_eq!(42, *c.gen().link)
});
t.join().unwrap()
}
Rustc complains:
error: `a` does not live long enough
--> src/lib.rs:53:21
|
53 | let mut c = a.lock().unwrap();
| ^ does not live long enough
54 | assert_eq!(42, *c.gen().link)
55 | });
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error: `c` does not live long enough
--> src/lib.rs:54:25
|
54 | assert_eq!(42, *c.gen().link)
| ^ does not live long enough
55 | });
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to 2 previous errors
Apparently Cache
values need to have 'static
lifetimes due to std::thread::spawn
.
While I understand the reasoning behind spawn
's type signature, I have been unable to write a cache which holds values of non-static lifetimes. This is especially puzzling to me because I can share Gen
itself across threads:
#[test]
fn test_gen() {
use std::sync::{Arc, Mutex};
use std::thread;
let a = Arc::new(Mutex::new(Gen { root: 42 }));
let t = thread::spawn(move || {
let mut c = a.lock().unwrap();
assert_eq!(42, *c.gen().link)
});
t.join().unwrap()
}
After all this I wonder―how does one write Cache
correctly?
Thank you for bearing with me. I look forward to your replies!