use std::fs::File;
 use std::io::prelude::*;
+use std::collections::HashMap;
 
 pub fn read_file(path: &str) -> Result<String, std::io::Error> {
     let mut s = String::new();
         y
     }
 }
+
+/// Wrap and cache an expensive calculation
+///
+/// This calls a closure just once for every distinct argument. Any subsequent
+/// call to `.value()` with the same argument uses the cached value.
+pub struct Cacher<T, A, V>
+where
+    T: Fn(A) -> V,
+    A: Eq + Copy + std::hash::Hash,
+    V: Copy,
+{
+    calc: T,
+    values: HashMap<A, V>,
+}
+
+impl<T, A, V> Cacher<T, A, V>
+where
+    T: Fn(A) -> V,
+    A: Eq + Copy + std::hash::Hash,
+    V: Copy,
+{
+    pub fn new(calc: T) -> Cacher<T, A, V> {
+        Cacher { calc, values: HashMap::new() }
+    }
+
+    pub fn value(&mut self, arg: A) -> V {
+        match self.values.get(&arg) {
+            Some(v) => *v,
+            None => {
+                let v = (self.calc)(arg);
+                self.values.insert(arg, v);
+                v
+            }
+        }
+    }
+}
 
     println!("longest string: {}", l);
 }
 
+fn test_closures() {
+    let mut expensive_int_result = Cacher::new(|x| {
+        println!("calculating expensive int result for {}", x);
+        2 * x
+    });
+
+    println!("1st int call for value 1: {}", expensive_int_result.value(1));
+    println!("2nd int call for value 1: {}", expensive_int_result.value(1));
+    println!("1st int call for value 2: {}", expensive_int_result.value(2));
+
+    let mut expensive_str_result = Cacher::new(|x: &str| {
+        println!("calculating expensive str result for {}", x);
+        x.len()
+    });
+
+    println!("1st int call for value abc: {}", expensive_str_result.value("abc"));
+    println!("2nd int call for value abc: {}", expensive_str_result.value("abc"));
+    println!("1st int call for value defg: {}", expensive_str_result.value("defg"));
+}
+
 fn main() {
     test_strings();
     test_vectors();
     test_hashmaps();
     test_files();
     test_generics();
+    test_closures();
 }
 
     assert_eq!(longest("abc", "def"), "def");
     assert_eq!(longest("abc", "defg"), "defg");
 }
+
+// FIXME: How to make this not unsafe?
+static mut CALLED: u32 = 0;
+
+#[test]
+fn test_cacher_int_int() {
+    unsafe { CALLED = 0; }
+    let mut cacher = Cacher::new(|x| {
+        unsafe { CALLED += 1; }
+        2 * x
+    });
+    assert_eq!(cacher.value(1), 2);
+    unsafe { assert_eq!(CALLED, 1); }
+    // second time cached
+    assert_eq!(cacher.value(1), 2);
+    unsafe { assert_eq!(CALLED, 1); }
+    // re-evaluated for new value
+    assert_eq!(cacher.value(-2), -4);
+    unsafe { assert_eq!(CALLED, 2); }
+    // old arg still cached
+    assert_eq!(cacher.value(1), 2);
+    unsafe { assert_eq!(CALLED, 2); }
+}
+
+#[test]
+fn test_cacher_str_usize() {
+    unsafe { CALLED = 0; }
+    let mut cacher = Cacher::new(|x: &str| {
+        unsafe { CALLED += 1; }
+        x.len()
+    });
+    assert_eq!(cacher.value("abc"), 3);
+    unsafe { assert_eq!(CALLED, 1); }
+    // second time cached
+    assert_eq!(cacher.value("abc"), 3);
+    unsafe { assert_eq!(CALLED, 1); }
+    // re-evaluated for new value
+    assert_eq!(cacher.value("defg"), 4);
+    unsafe { assert_eq!(CALLED, 2); }
+    // old arg still cached
+    assert_eq!(cacher.value("abc"), 3);
+    unsafe { assert_eq!(CALLED, 2); }
+}