use std::thread; use std::time::Duration; use std::collections::HashMap; struct Cacher where T: Fn(&P) -> R, P: std::cmp::Eq + std::hash::Hash { calculation: T, values: HashMap, } impl Cacher where T: Fn(&P) -> R, P: std::cmp::Eq + std::hash::Hash { fn new(calculation: T) -> Cacher { Cacher { calculation, values: HashMap::new(), } } fn value(&mut self, arg: P) -> &R { use std::collections::hash_map::Entry; match self.values.entry(arg) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { let v = (self.calculation)(entry.key()); entry.insert(v) } } } } fn generate_workout(intensity: u32, random_number: u32) { let mut expensive_result = Cacher::new(|num| { println!("calculating..."); thread::sleep(Duration::from_secs(2)); *num }); if intensity < 25 { println!( "Today, do {} pushups!", expensive_result.value(intensity) ); println!( "Next, do {} situps!", expensive_result.value(intensity) ); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!( "Today, run for {} minutes!", expensive_result.value(intensity) ); } } } fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout( simulated_user_specified_value, simulated_random_number ); } #[cfg(test)] mod tests { use super::*; #[test] fn call_with_different_values() { let mut c = Cacher::new(|a| *a); let _v1 = c.value(1); let v2 = c.value(2); assert_eq!(*v2, 2); } #[test] fn value_can_differ_return_type_from_parameter_type() { let mut c = Cacher::new(|_a| String::from("hello")); let v = c.value(5); assert_eq!(*v, "hello"); } }