Hash Maps and Hash Sets

or_insert_with()

The .or_insert_with(f) and .or_insert_with_key(f) methods on Entry<'a, K, V> lazily construct a fallback value using an FnOnce closure only when the entry is Vacant, avoiding wasted allocations or computations on cache hits.

Signatures

impl<'a, K, V> Entry<'a, K, V> {
    pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V;

    pub fn or_insert_with_key<F: FnOnce(&K) -> V>(self, default: F) -> &'a mut V;
}
Method When Closure Runs Closure Parameter Typical Use Case
e.or_insert(val) (Always evaluated eagerly before call) — Cheap Copy literals (0, true, None)
e.or_insert_with(|| ...) Only when e is Vacant () Allocating Vec::with_capacity, String, or computing an expensive value
e.or_insert_with_key(|k| ...) Only when e is Vacant &K (shared reference to the entry's key) Deriving the initial value from the key itself without cloning the key

Lazy Initialization Only on Cache Misses

use std::collections::HashMap;

fn expensive_compute(key: &str) -> String {
    println!("  [computing value for '{key}'...]");
    format!("computed:{key}")
}

fn main() {
    let mut cache: HashMap<&str, String> = HashMap::new();

    // 1st lookup of "user_1" -> Vacant -> closure runs:
    let v1 = cache
        .entry("user_1")
        .or_insert_with(|| expensive_compute("user_1"));
    println!("1st result: {v1}");

    // 2nd lookup of "user_1" -> Occupied -> closure does NOT run!
    let v2 = cache
        .entry("user_1")
        .or_insert_with(|| expensive_compute("user_1"));
    println!("2nd result: {v2}");
}
  [computing value for 'user_1'...]
1st result: computed:user_1
2nd result: computed:user_1

Deriving the Initial Value from the Key (or_insert_with_key)

When a non-Copy key (such as an owned String) is moved into map.entry(key), you can no longer read key inside a regular .or_insert_with(|| ...) closure because key was moved into the Entry. or_insert_with_key(|k| ...) passes &K directly from the VacantEntry to your closure:

use std::collections::HashMap;

fn main() {
    let mut word_lengths: HashMap<String, usize> = HashMap::new();

    for raw in ["rust", "collections", "rust"] {
        let owned_key = String::from(raw);

        // `owned_key` is moved into `entry(...)`, and `or_insert_with_key`
        // borrows `&String` as `k` to compute `k.len()` only if vacant:
        let len = *word_lengths
            .entry(owned_key)
            .or_insert_with_key(|k| k.len());

        println!("{raw:<12} -> len = {len}");
    }
}
rust         -> len = 4
collections  -> len = 11
rust         -> len = 4