If you cancel the return value requirement, you do not need to execute any clones using Entry :
use std::{ collections::{hash_map::Entry, HashMap}, fmt::Display, hash::Hash, thread, time::Duration, }; struct Cacher<P, R, T> where T: Fn(&P) -> R, P: Eq + Hash, { calculation: T, values: HashMap<P, R>, } impl<P, R, T> Cacher<P, R, T> where T: Fn(&P) -> R, P: Eq + Hash, { fn new(calculation: T) -> Cacher<P, R, T> { Cacher { calculation, values: HashMap::new(), } } fn value<'a>(&'a mut self, key: P) -> &'a R { let calculation = &self.calculation; match self.values.entry(key) { Entry::Occupied(e) => e.into_mut(), Entry::Vacant(e) => { let result = (calculation)(e.key()); e.insert(result) } } } } fn main() { let mut cacher1 = Cacher::new(|num: &u32| -> u32 { println!("calculating slowly..."); thread::sleep(Duration::from_secs(1)); *num }); calculate_and_print(10, &mut cacher1); calculate_and_print(20, &mut cacher1); calculate_and_print(10, &mut cacher1); let mut cacher2 = Cacher::new(|str: &&str| -> usize { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); str.len() }); calculate_and_print("abc", &mut cacher2); calculate_and_print("defghi", &mut cacher2); calculate_and_print("abc", &mut cacher2); } fn calculate_and_print<P, R, T>(intensity: P, cacher: &mut Cacher<P, R, T>) where T: Fn(&P) -> R, P: Eq + Hash, R: Display, { println!("{}", cacher.value(intensity)); }
Then you can wrap this in another structure that executes the clone:
struct ValueCacher<P, R, T> where T: Fn(&P) -> R, P: Eq + Hash, R: Clone, { cacher: Cacher<P, R, T>, } impl<P, R, T> ValueCacher<P, R, T> where T: Fn(&P) -> R, P: Eq + Hash, R: Clone, { fn new(calculation: T) -> Self { Self { cacher: Cacher::new(calculation), } } fn value(&mut self, key: P) -> R { self.cacher.value(key).clone() } }