evictor
Generic in‑memory caches with pluggable eviction policies. One uniform API, generic over key + value, O(1) average core ops, and interchangeable eviction behavior chosen by a type alias.
Policies at a glance
| Policy | Evicts | Primary signal | Notes |
|---|---|---|---|
| Lru | Least recently used | Recency | Doubly linked list of entries |
| Mru | Most recently used | Recency (inverted) | Same structure as LRU, opposite victim |
| Lfu | Least frequently used | Frequency + recency tie break | Frequency buckets; O(1) amortized updates |
| Fifo | Oldest inserted | Insertion order | Queue semantics (head victim) |
| Lifo | Newest inserted | Insertion order | Stack semantics (head victim) |
| Random | Random entry | RNG | Requires rand feature (default) |
| Sieve | First unvisited (2nd chance) | Visited bit pass | Implements NSDI'24 SIEVE (see paper) |
Max Capacity
The maximum capacity of a cache must be specified at creation time (minimum 1, maximum 2^32 - 1),
although it may later be changed with set_capacity(). Insertion of a new entry when at capacity
evicts an existing entry according to the policy.
Size overhead
All policies use a custom linked hash map implementation, which has a per-entry overhead of 4 u32s (16 bytes) plus the size of the key + value. LFU has additional overhead for frequency buckets, which is small compared to the entry overhead unless there are many unique frequencies.
The four u32s are for the doubly linked list (2), an index into a compressed list of linked list pointers, and of course, the actual entry in the reverse mapping. These are needed to achieve O(1) updates for all policies.
Quick start
LRU example (API identical for all policies)
use NonZeroUsize;
use Lru;
let mut cache = new;
cache.insert;
cache.insert;
cache.insert;
cache.get; // mark as recently used
cache.insert; // evicts key 2
assert!;
Swap Lru for Mru, Lfu, Fifo, Lifo, Random, or Sieve to change behavior.
Policy nuances
use NonZeroUsize;
use ;
use Random;
let mut mru = new;
mru.insert;
mru.insert;
mru.get; // 'a' now MRU
mru.insert; // evicts 'a'
let mut lfu = new;
lfu.insert;
lfu.insert;
lfu.get; // freq(a)=1
lfu.insert; // evicts 'b'
let mut sieve = new;
sieve.insert;
sieve.insert;
sieve.get; // mark visited
sieve.insert; // evicts 2 (unvisited)
Building from iterators
FromIterator collects all pairs setting capacity to the number of unique items in the iterator
(minimum 1).
use ;
use Random;
let data = ;
let lru: = data.into_iter.collect;
assert_eq!;
Iteration order
iter() and into_iter() yield entries in eviction order (tail first) except Random (arbitrary /
randomized).
Performance
Average insert, get, peek, remove, pop: O(1). Worst cases inherit IndexMap behavior. LFU
adjustments are O(1) amortized (bucket promotion); SIEVE's tail() may scan O(n) in degenerate
patterns.
Mutation‑aware access (peek_mut)
peek_mut only reorders if the value is mutated (tracked by Drop). Plain get_mut always touches.
Capacity management
set_capacity(new)adjusts capacity, evicting as needed.evict_to_size(n)trims down to a target size.evict_n_entries(k)evicts up tokvictims.
Feature flags
| Feature | Default | Purpose |
|---|---|---|
ahash |
Yes | Faster hashing (ahash::RandomState) |
rand |
Yes | Enables Random policy |
statistics |
No | Enables cache statistics tracking (hits, misses, etc.) |
internal-debugging |
No | Potentially expensive invariant checking (debug builds only) |
Safety & Guarantees
- No unsafe code in library (although e.g. hashbrown may use some).
License
MIT OR Apache‑2.0. See LICENSE-MIT and LICENSE-APACHE.