Skip to content

Instantly share code, notes, and snippets.

@bryanmaina
Last active January 9, 2025 20:31
Show Gist options
  • Save bryanmaina/25b4a505996fc22896703b62acf1bdac to your computer and use it in GitHub Desktop.
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.
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