Last active
January 9, 2025 20:31
-
-
Save bryanmaina/25b4a505996fc22896703b62acf1bdac to your computer and use it in GitHub Desktop.
Taking Himanshu Kapoor (30 Days of Rust: Day 19 - Closures) tip and making it as complicated as possible because why not.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{ | |
collections::{HashMap, VecDeque}, | |
sync::{Arc, Mutex}, | |
thread, | |
time::Duration, | |
}; | |
// Struct using a closure | |
struct Cacher<T> | |
where | |
T: Fn(u32) -> u32, | |
{ | |
calculation: T, | |
cache: Mutex<CacheData<u32, u32>>, | |
capacity: usize, | |
} | |
struct CacheData<K, V> | |
where | |
K: Eq + std::hash::Hash + Copy, | |
{ | |
map: HashMap<K, V>, | |
queue: VecDeque<K>, | |
} | |
impl<K, V> CacheData<K, V> | |
where | |
K: Eq + std::hash::Hash + Copy, | |
{ | |
fn new() -> Self { | |
CacheData { | |
map: HashMap::new(), | |
queue: VecDeque::new(), | |
} | |
} | |
fn get(&mut self, key: &K) -> Option<&V> { | |
if let Some(value) = self.map.get(key) { | |
//if the key exists, we need to reinsert to the back so that we track that it was used. | |
if let Some(index) = self.queue.iter().position(|x| x == key) { | |
let key = self.queue.remove(index).unwrap(); | |
self.queue.push_back(key); | |
} | |
Some(value) | |
} else { | |
None | |
} | |
} | |
fn insert(&mut self, key: K, value: V, capacity: usize) { | |
if let Some(index) = self.queue.iter().position(|x| x == &key) { | |
self.queue.remove(index); | |
} | |
self.queue.push_back(key); | |
self.map.insert(key, value); | |
if self.map.len() > capacity { | |
if let Some(oldest) = self.queue.pop_front() { | |
self.map.remove(&oldest); | |
} | |
} | |
} | |
} | |
impl<T> Cacher<T> | |
where | |
T: Fn(u32) -> u32, | |
{ | |
fn new(calculation: T, capacity: usize) -> Cacher<T> { | |
Cacher { | |
calculation, | |
cache: Mutex::new(CacheData::new()), | |
capacity, | |
} | |
} | |
fn value(&self, arg: u32) -> u32 { | |
let mut cache = self.cache.lock().unwrap(); | |
if let Some(v) = cache.get(&arg) { | |
return *v; | |
} | |
let v = (self.calculation)(arg); | |
cache.insert(arg, v, self.capacity); | |
v | |
} | |
} | |
fn main() { | |
// Create a Cacher with a capacity of 3 | |
let expensive_result = Cacher::new( | |
|num| -> u32 { | |
println!( | |
"Calculating slowly...{} * 10 on {:?}", | |
num, | |
thread::current().id() | |
); | |
thread::sleep(Duration::from_secs(2)); | |
num * 10 | |
}, | |
3, | |
); | |
// Create an Arc so it can be used in multiple threads | |
let expensive_result = Arc::new(expensive_result); | |
// Clone the Arc to be used by the threads | |
let expensive_result_cloned = expensive_result.clone(); | |
// Spawn threads to execute value in parallel | |
let handle = thread::spawn(move || { | |
let values = vec![10, 11, 12, 13, 10]; | |
for val in values { | |
println!( | |
"== {:?}, Cached result for {}: {}", | |
thread::current().id(), | |
val, | |
expensive_result_cloned.value(val) | |
); | |
} | |
}); | |
// Clone the Arc to be used by another thread | |
let expensive_result_cloned_2 = expensive_result.clone(); | |
let handle2 = thread::spawn(move || { | |
let values = vec![10, 11, 12, 13, 10]; | |
for val in values { | |
println!( | |
"-- {:?}, Cached result for {}: {}", | |
thread::current().id(), | |
val, | |
expensive_result_cloned_2.value(val) | |
); | |
} | |
}); | |
handle.join().unwrap(); | |
handle2.join().unwrap(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment