What is the difference between lru::LruCache::promote and get for explicitly updating LRU ordering?

get retrieves a value while updating its position to most-recently-used, while promote updates the position without returning the value. Both methods affect the LRU ordering, but promote is designed for cases where you need to manipulate the ordering without incurring the cost of cloning or returning the value.

LRU Cache Basics

use lru::LruCache;
 
fn lru_basics() {
    // LRU cache evicts least recently used items when full
    let mut cache: LruCache<&str, i32> = LruCache::new(3);
    
    // Insert items
    cache.put("a", 1);
    cache.put("b", 2);
    cache.put("c", 3);
    
    // Cache is now: [a:1, b:2, c:3] (oldest to newest)
    
    // Accessing updates ordering
    cache.get(&"a");  // "a" becomes most recent
    
    // Order now: [b:2, c:3, a:1] (a moved to end)
}

LRU caches maintain access order, moving items to "most recent" on access.

The get Method

use lru::LruCache;
 
fn get_behavior() {
    let mut cache: LruCache<&str, i32> = LruCache::new(3);
    cache.put("a", 1);
    cache.put("b", 2);
    cache.put("c", 3);
    
    // get returns Option<&V> and updates ordering
    let value = cache.get(&"a");  // Returns Some(&1)
    
    // Side effect: "a" is now most recently used
    // Order changed: [b:2, c:3, a:1]
    
    assert_eq!(value, Some(&1));
    
    // get returns None if key doesn't exist
    let missing = cache.get(&"d");
    assert_eq!(missing, None);
}

get returns the value reference and moves the entry to most-recently-used position.

The promote Method

use lru::LruCache;
 
fn promote_behavior() {
    let mut cache: LruCache<&str, i32> = LruCache::new(3);
    cache.put("a", 1);
    cache.put("b", 2);
    cache.put("c", 3);
    
    // promote updates ordering WITHOUT returning the value
    cache.promote(&"a");
    
    // Order changed: [b:2, c:3, a:1]
    // No value returned
    
    // promote returns bool: true if key existed
    let existed = cache.promote(&"a");  // true
    let missing = cache.promote(&"d");  // false
}

promote updates ordering and returns bool indicating whether the key existed.

Key Differences

use lru::LruCache;
 
fn comparison() {
    let mut cache: LruCache<&str, String> = LruCache::new(3);
    cache.put("a", "expensive to clone".to_string());
    
    // ┌─────────────────────────────────────────────────────────────────────────┐
    // │ Aspect               │ get                     │ promote               │
    // ├─────────────────────────────────────────────────────────────────────────┤
    // │ Return type          │ Option<&V>              │ bool                  │
    // │ Ordering update      │ Yes                     │ Yes                   │
    // │ Returns value        │ Yes                     │ No                    │
    // │ Purpose              │ Access + update         │ Update only           │
    // │ When key missing     │ Returns None           │ Returns false         │
    // └─────────────────────────────────────────────────────────────────────────┘
    
    // Both update ordering
    cache.get(&"a");      // Returns Some(&"expensive to clone")
    cache.promote(&"a");  // Returns true, no value returned
}

Both methods update ordering; the difference is what they return.

When to Use promote

use lru::LruCache;
 
fn when_to_use_promote() {
    let mut cache: LruCache<&str, String> = LruCache::new(3);
    
    // Store expensive-to-clone values
    cache.put("key", "very large string data".to_string());
    
    // Scenario 1: You already have a reference to the value
    // and just need to update ordering
    if let Some(value) = cache.peek(&"key") {
        // Do something with value
        println!("Value: {}", value);
        
        // Now update ordering without getting the value again
        cache.promote(&"key");
    }
    
    // Scenario 2: External access tracking
    // You know an item was accessed externally and want to refresh it
    fn on_external_access(cache: &mut LruCache<&str, Data>, key: &str) {
        // Just update ordering, don't need the value
        cache.promote(key);
    }
}

Use promote when you need to update ordering but don't need the value returned.

The peek Method (No Ordering Update)

use lru::LruCache;
 
fn peek_vs_get_vs_promote() {
    let mut cache: LruCache<&str, i32> = LruCache::new(3);
    cache.put("a", 1);
    cache.put("b", 2);
    cache.put("c", 3);
    
    // ┌─────────────────────────────────────────────────────────────────────────┐
    // │ Method    │ Returns     │ Updates Ordering │ Use Case                  │
    // ├─────────────────────────────────────────────────────────────────────────┤
    // │ get       │ Option<&V>  │ Yes              │ Normal access            │
    // │ peek      │ Option<&V>  │ No               │ Inspect without affecting │
    // │ promote   │ bool        │ Yes              │ Update ordering only     │
    // └─────────────────────────────────────────────────────────────────────────┘
    
    // peek: get without updating ordering
    let value = cache.peek(&"a");  // Returns Some(&1)
    // Order unchanged: [a:1, b:2, c:3]
    
    // get: get with ordering update
    let value = cache.get(&"a");    // Returns Some(&1)
    // Order changed: [b:2, c:3, a:1]
    
    // promote: update ordering without returning value
    let existed = cache.promote(&"b");  // Returns true
    // Order changed: [c:3, a:1, b:2]
}

Three methods for three different access patterns: get (access + update), peek (access only), promote (update only).

Combining peek and promote

use lru::LruCache;
 
fn peek_and_promote() {
    let mut cache: LruCache<&str, String> = LruCache::new(3);
    cache.put("key", "data".to_string());
    
    // Common pattern: peek first, conditionally promote
    if let Some(value) = cache.peek(&"key") {
        if value.starts_with("important") {
            // Only update ordering for important items
            cache.promote(&"key");
        }
    }
    
    // This is equivalent to a conditional get:
    if let Some(value) = cache.peek(&"key") {
        if value.starts_with("important") {
            cache.get(&"key");  // Would update ordering
        }
    }
}

Combining peek and promote allows fine-grained control over ordering updates.

Performance Considerations

use lru::LruCache;
 
fn performance() {
    let mut cache: LruCache<u64, LargeStruct> = LruCache::new(1000);
    
    // Assume LargeStruct is expensive to clone
    // and we want to track access patterns
    
    // Using get: returns reference (no clone), but still has lookup overhead
    if let Some(value) = cache.get(&42) {
        // Use value
    }
    
    // Using promote: no reference returned, minimal overhead
    // Useful when you just need to mark something as recently used
    cache.promote(&42);
}
 
struct LargeStruct {
    data: [u8; 1024],
}

promote avoids the overhead of returning a reference when you only need ordering updates.

Use Cases for promote

use lru::LruCache;
 
fn use_cases() {
    let mut cache: LruCache<&str, Data> = LruCache::new(100);
    
    // 1. Background refresh patterns
    fn background_refresh(cache: &mut LruCache<&str, Data>, key: &str) {
        // External system indicates data was accessed
        // We want to keep it in cache but don't need the value
        cache.promote(key);
    }
    
    // 2. Prefetching with ordering
    fn prefetch_accessed(cache: &mut LruCache<&str, Data>, accessed_keys: &[&str]) {
        // Update ordering for all accessed keys
        for key in accessed_keys {
            cache.promote(key);
        }
    }
    
    // 3. Manual LRU manipulation
    fn explicit_ordering(cache: &mut LruCache<&str, Data>) {
        // Move specific items to front/back of LRU
        cache.promote(&"important");  // Make it most recent
    }
    
    // 4. Avoiding borrow checker issues
    fn complex_scenario(cache: &mut LruCache<&str, Data>) {
        // When you have a reference to the value already
        // and just need to update ordering
        if let Some(value) = cache.peek(&"key") {
            process_value(value);
            // Can't call get() here (would need &mut cache)
            // But we can call promote after processing
        }
        cache.promote(&"key");
    }
}
 
struct Data;
fn process_value(_: &Data) {}

promote is useful for background tasks, explicit ordering, and avoiding borrow conflicts.

Implementation Details

use lru::LruCache;
 
fn implementation_notes() {
    // Both get and promote use the same underlying mechanism
    // to update the LRU ordering (moving node to front of list)
    
    // The difference is purely in return value:
    // - get: returns Option<&V> (reference to value)
    // - promote: returns bool (did the key exist)
    
    // Time complexity is the same: O(1)
    // Both operations are constant time
    
    let mut cache: LruCache<i32, i32> = LruCache::new(100);
    
    // These have the same performance characteristics
    cache.get(&1);      // O(1) lookup + ordering update
    cache.promote(&1);  // O(1) lookup + ordering update
    
    // The difference is just what they return
}

Both methods have identical performance characteristics; the difference is return value.

Working with Contains

use lru::LruCache;
 
fn contains_pattern() {
    let mut cache: LruCache<&str, i32> = LruCache::new(3);
    cache.put("a", 1);
    
    // contains does NOT update ordering
    let exists = cache.contains(&"a");  // true, but order unchanged
    
    // If you want to check existence AND update ordering:
    // Option 1: use get
    if cache.get(&"a").is_some() {
        // Exists and ordering updated
    }
    
    // Option 2: use contains + promote
    if cache.contains(&"a") {
        cache.promote(&"a");  // Update ordering separately
    }
}

contains checks existence without updating ordering; combine with promote for explicit control.

Iteration and Promotion

use lru::LruCache;
 
fn iteration_promotion() {
    let mut cache: LruCache<i32, String> = LruCache::new(5);
    
    for i in 0..5 {
        cache.put(i, format!("value{}", i));
    }
    
    // Promote even numbers
    for key in 0..5 {
        if key % 2 == 0 {
            cache.promote(&key);
        }
    }
    
    // Even numbers are now most recently used
    // Order: [1, 3, 0, 2, 4] (roughly)
}

Use promote in loops when you need to update ordering without accessing values.

Complete Example

use lru::LruCache;
 
struct CacheManager {
    cache: LruCache<String, Vec<u8>>,
}
 
impl CacheManager {
    fn new(capacity: usize) -> Self {
        Self {
            cache: LruCache::new(capacity),
        }
    }
    
    // Normal access: get value and update ordering
    fn get(&mut self, key: &str) -> Option<&Vec<u8>> {
        self.cache.get(&key.to_string())
    }
    
    // Inspect without affecting ordering
    fn peek(&self, key: &str) -> Option<&Vec<u8>> {
        self.cache.peek(&key.to_string())
    }
    
    // Mark as recently used (from external signal)
    fn mark_accessed(&mut self, key: &str) -> bool {
        self.cache.promote(&key.to_string())
    }
    
    // Batch promote multiple keys
    fn promote_batch(&mut self, keys: &[&str]) {
        for key in keys {
            self.cache.promote(&key.to_string());
        }
    }
}
 
fn main() {
    let mut manager = CacheManager::new(100);
    
    // Insert data
    manager.cache.put("file1".to_string(), vec
![1, 2, 3]);
    
    // Normal access
    if let Some(data) = manager.get("file1") {
        println!("Data: {:?}", data);
    }
    
    // External access notification
    manager.mark_accessed("file1");
    
    // Batch promotion
    manager.promote_batch(&["file1", "file2", "file3"]);
}

Summary

use lru::LruCache;
 
fn summary() {
    // ┌─────────────────────────────────────────────────────────────────────────┐
    // │ Method    │ Returns     │ Updates Order │ When to Use                  │
    // ├─────────────────────────────────────────────────────────────────────────┤
    // │ get       │ Option<&V>  │ Yes           │ Normal access, need value    │
    // │ peek      │ Option<&V>  │ No            │ Inspect without affecting    │
    // │ promote   │ bool        │ Yes           │ Update ordering only        │
    // │ contains  │ bool        │ No            │ Check existence             │
    // └─────────────────────────────────────────────────────────────────────────┘
    
    // Key distinctions:
    // - get: returns value + updates ordering
    // - promote: updates ordering, returns bool (existed)
    // - peek: returns value, no ordering update
    // - contains: returns bool, no ordering update
    
    // Use promote when:
    // 1. You need to update ordering but not access the value
    // 2. External system signals access (background refresh)
    // 3. Batch updating multiple entries' ordering
    // 4. Avoiding duplicate lookups when value is already known
}

Key insight: get and promote both update LRU ordering, but get returns the value while promote returns only whether the key existed. Use get for normal access patterns where you need the value, peek to inspect without affecting ordering, and promote when you need to explicitly update ordering without incurring the overhead or semantics of returning the value. The three methods provide complete control over the LRU cache's ordering behavior.