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.

RustData Fetching

Code

snippet
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());
}