use std::thread;
use std::time::Duration;
use std::collections::HashMap;
struct Cacher<T, P, R>
where T: Fn(&P) -> R,
P: std::cmp::Eq + std::hash::Hash
{
calculation: T,
values: HashMap<P, R>,
}
impl<T, P, R> Cacher<T, P, R>
where T: Fn(&P) -> R,
P: std::cmp::Eq + std::hash::Hash
{
fn new(calculation: T) -> Cacher<T, P, R> {
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");
}
}