cacher.rs
@@ -0,0 +1,95 @@
+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");
+ }
+}