lru-rs

repository·master·Indexed 21 days ago

https://github.com/jeromefroe/lru-rs

An efficient implementation of a Least Recently Used (LRU) cache in Rust (version 0.18.2). It provides O(1) time complexity for core operations including insertion, retrieval, and eviction. The LruCache API supports fixed or unbounded capacity, custom hashers, and a variety of methods for managing key-value pairs, such as put, push, peek, and get_or_insert patterns.

Tokens
5.9K
Snippets
23
Records
23
Agent score
24%

What's inside lru

  1. Instantiate and use an LruCache

    master

    To use LruCache, you must provide a capacity using NonZeroUsize. The cache will automatically evict the least recently used items when the capacity is reached. The following example demonstrates instantiation, insertion, retrieval, eviction behavior, and mutable access.

    extern crate lru;
    
    use lru::LruCache;
    use std::num::NonZeroUsize;
    
    fn main() {
        let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
        cache.put("apple", 3);
        cache.put("banana", 2);
    
        assert_eq!(*cache.get(&"apple").unwrap(), 3);
        assert_eq!(*cache.get(&"banana").unwrap(), 2);
        assert!(cache.get(&"pear").is_none());
    
        assert_eq!(cache.put("banana", 4), Some(2));
        assert_eq!(cache.put("pear", 5), None);
    
        assert_eq!(*cache.get(&"pear").unwrap(), 5);
        assert_eq!(*cache.get(&"banana").unwrap(), 4);
        assert!(cache.get(&"apple").is_none());
    
        {
            let v = cache.get_mut(&"banana").unwrap();
            *v = 6;
        }
    
        assert_eq!(*cache.get(&"banana").unwrap(), 6);
    }
  2. Use the LruCache API

    master

    The LruCache provides a fixed-capacity Least Recently Used (LRU) cache. It supports several O(1) operations for managing key-value pairs:

    • new(capacity: NonZeroUsize): Creates a new cache with the specified capacity. The capacity must be a NonZeroUsize.
    • put(K, V): Inserts a key-value pair into the cache. If the key already exists, it updates the value and returns the old value wrapped in Some. If the insertion causes the cache to exceed capacity, the least recently used item is evicted. If no item was evicted, it returns None.
    • get(&K): Returns a reference to the value associated with the key. This operation marks the key as most recently used.
    • get_mut(&K): Returns a mutable reference to the value associated with the key. This operation marks the key as most recently used.
    • pop(): Removes and returns the least recently used (LRU) key-value pair.
    use lru::LruCache;
    use std::num::NonZeroUsize;
    
    let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
    
    // Insert values
    cache.put("apple", 3);
    cache.put("banana", 2);
    
    // Retrieve values (marks as recently used)
    let val = cache.get(&"apple");
    
    // Update values
    cache.put("banana", 4);
    
    // Mutable access
    if let Some(v) = cache.get_mut(&"banana") {
        *v = 6;
    }
  3. Insert items into LruCache using put() and push()

    master

    There are two primary ways to insert items, depending on whether you want to capture evicted entries.

    • put(k: K, v: V) -> Option<V>: Inserts a key-value pair. If the key already exists, it updates the value and returns the old value. If the cache is at capacity, the least recently used item is evicted (but not returned).
    • push(k: K, v: V) -> Option<(K, V)>: Similar to put, but if an entry is evicted due to capacity or if the key already exists, it returns the evicted/old key-value pair as Some((K, V)).
    use lru::LruCache;
    use std::num::NonZeroUsize;
    
    let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
    
    assert_eq!(None, cache.put(1, "a"));
    assert_eq!(None, cache.put(2, "b"));
    // Updates existing key, returns old value
    assert_eq!(Some("b"), cache.put(2, "beta"));
    
    // push returns the evicted or old entry
    assert_eq!(Some((2, "beta")), cache.push(2, "beta"));
    // Returns the LRU entry (1, "a") because capacity is 2
    assert_eq!(Some((1, "a")), cache.push(3, "alpha"));
  4. Manage LruCache capacity and size

    master

    Use these methods to inspect or modify the cache constraints:

    • len(): Returns the current number of key-value pairs.
    • is_empty(): Returns true if the cache contains no items.
    • cap(): Returns the maximum capacity (NonZeroUsize).
    • resize(new_cap): Changes the capacity. If the new capacity is smaller than the current size, excess entries are evicted starting from the LRU position.
    use lru::LruCache;
    use std::num::NonZeroUsize;
    let mut cache: LruCache<isize, &str> = LruCache::new(NonZeroUsize::new(2).unwrap());
    
    cache.put(1, "a");
    cache.resize(NonZeroUsize::new(4).unwrap());
    assert_eq!(cache.cap().get(), 4);
  5. Iterate over cache entries

    master

    The LruCache supports several iteration patterns:

    • iter(): Returns an iterator over (&K, &V) pairs, typically from MRU to LRU.
    • iter_mut(): Returns an iterator over (&K, &mut V) pairs.
    • into_iter(): Consumes the cache and returns an iterator over (K, V) pairs.
    • next_back(): Available on iterators to traverse in reverse order (LRU to MRU).
    let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
    cache.put("a", 1);
    cache.put("b", 2);
    
    for (key, value) in cache.iter() {
        println!("{}: {}", key, value);
    }
  6. Remove LRU or MRU items with `pop_lru()` and `pop_mru()`

    master

    To remove items based on their usage order:

    • pop_lru(): Removes and returns the least recently used item (the oldest).
    • pop_mru(): Removes and returns the most recently used item (the newest).
    let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
    cache.put(1, "a");
    cache.put(2, "b");
    
    let oldest = cache.pop_lru(); // Some((1, "a"))
    let newest = cache.pop_mru(); // Some((2, "b"))
  7. Retrieve values with `get()` and `get_mut()`

    master

    Access values in the cache without changing their LRU position using get(&key). To modify a value in place, use get_mut(&key). Both methods return an Option containing a reference to the value. Note that get and get_mut do not change the order of elements in the LRU list; use peek or find_and_promote if you need to change the order.

    let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
    cache.put("apple", 1);
    
    // Read access
    let val = cache.get(&"apple"); // Some(&1)
    
    // Mutable access
    if let Some(v) = cache.get_mut(&"apple") {
        *v = 2;
    }
  8. Get or insert a value by reference in LruCache

    master

    The try_get_or_insert_mut_ref method allows querying the cache using a borrowed type Q that can be converted into the owned key type K. If the key is missing, the provided closure f is called to produce a value. This method is efficient for types like Rc<String> because it minimizes cloning of the key.

    use lru::LruCache;
    use std::num::NonZeroUsize;
    use std::rc::Rc;
    
    let key2 = Rc::new("2".to_owned());
    let mut cache = LruCache::<Rc<String>, String>::new(NonZeroUsize::new(2).unwrap());
    let b = || -> Result<String, ()> { Ok("Two".to_owned()) };
    
    if let Ok(v) = cache.try_get_or_insert_mut_ref(&key2, b) {
        *v = "New two".to_owned();
    }
  9. Promote and Demote keys in LruCache

    master

    You can manually manipulate the LRU order of specific keys:

    • promote(&k): Moves the key to the most recently used position. Returns true if the key existed and was promoted.
    • demote(&k): Moves the key to the least recently used position. Returns true if the key existed and was demoted.
    use lru::LruCache;
    use std::num::NonZeroUsize;
    let mut cache = LruCache::new(NonZeroUsize::new(3).unwrap());
    
    cache.put(1, "a");
    cache.put(2, "b");
    cache.put(3, "c");
    
    assert!(cache.promote(&3)); // 3 is now MRU
    assert!(cache.demote(&1)); // 1 is now LRU
  10. Remove items with `pop()` and `pop_entry()`

    master

    Use pop(&key) to remove a specific key and return its value. Use pop_entry(&key) to remove a specific key and return both the key and the value as a tuple. Both methods return an Option.

    let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
    cache.put("apple", "red");
    
    let val = cache.pop(&"apple"); // Some("red")
    let entry = cache.pop_entry(&"apple"); // Some(("apple", "red"))
  11. Remove items from LruCache

    master

    Several methods allow removing items from the cache:

    • pop(&k): Removes and returns the value Option<V> for the given key.
    • pop_entry(&k): Removes and returns both the key and value Option<(K, V)> for the given key.
    • pop_lru(): Removes and returns the least recently used key-value pair Option<(K, V)>.
    • pop_mru(): Removes and returns the most recently used key-value pair Option<(K, V)>.
    • clear(): Removes all entries from the cache.
    use lru::LruCache;
    use std::num::NonZeroUsize;
    let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
    
    cache.put(2, "a");
    assert_eq!(cache.pop(&2), Some("a"));
    assert_eq!(cache.pop(&2), None);
  12. Get or insert a value with a key in LruCache

    master

    If a key is missing from the cache, try_get_or_insert_mut_with_key uses a provided closure to generate a value. The closure receives a reference to the key. If the closure succeeds, the new value is inserted into the cache and a mutable reference is returned. If the closure returns an error, the error is returned. If the key already exists, it is moved to the head of the LRU list and a mutable reference to the existing value is returned.

    use lru::LruCache;
    use std::num::NonZeroUsize;
    let mut cache = LruCache::new(NonZeroUsize::new(2).unwrap());
    
    cache.put("Two", 3);
    
    let len = |k: &&str| -> Result<usize, String> { Ok(k.len()) };
    assert_eq!(cache.try_get_or_insert_mut_with_key("Two", len), Ok(&mut 3));