LRU Cache with Capacity and TTL
Implement an LRU cache with a hash map and a recency queue, evicting by capacity and expiring entries by timestamp.
Code
use std::collections::{HashMap, VecDeque};
use std::hash::Hash;
use std::time::{Duration, Instant};
struct LruCache<K, V> {
capacity: usize,
ttl: Duration,
entries: HashMap<K, (V, Instant)>,
order: VecDeque<K>,
}
impl<K: Eq + Hash + Clone, V: Clone> LruCache<K, V> {
fn new(capacity: usize, ttl: Duration) -> Self {
assert!(capacity > 0, "capacity must be positive");
Self {
capacity,
ttl,
entries: HashMap::new(),
order: VecDeque::new(),
}
}
fn get(&mut self, key: &K) -> Option<V> {
let expired = match self.entries.get(key) {
Some((_, expires_at)) => Instant::now() >= *expires_at,
None => false,
};
if expired {
self.remove(key);
return None;
}
let value = self.entries.get(key).map(|(value, _)| (*value).clone())?;
self.touch(key);
Some(value)
}
fn put(&mut self, key: K, value: V) {
self.entries
.insert(key.clone(), (value, Instant::now() + self.ttl));
self.touch(&key);
while self.entries.len() > self.capacity {
match self.order.pop_front() {
Some(oldest) => {
self.entries.remove(&oldest);
}
None => break,
}
}
}
fn touch(&mut self, key: &K) {
self.order.retain(|existing| existing != key);
self.order.push_back((*key).clone());
}
fn remove(&mut self, key: &K) {
self.entries.remove(key);
self.order.retain(|existing| existing != key);
}
fn len(&self) -> usize {
self.entries.len()
}
}
fn main() {
let mut cache = LruCache::new(2, Duration::from_millis(50));
cache.put("a", 1);
cache.put("b", 2);
println!("a -> {:?}", cache.get(&"a"));
cache.put("c", 3);
println!("b -> {:?}", cache.get(&"b"));
std::thread::sleep(Duration::from_millis(60));
println!("a -> {:?}", cache.get(&"a"));
println!("len = {}", cache.len());
}
Report a problem with this exampleOpens a pre-filled issue on GitHub.