Standard Collections

Collection Entry APIs

Both HashMap<K, V> and BTreeMap<K, V> provide an Entry API (map.entry(key)) that performs a single key lookup and returns an Entry<'_, K, V> view representing either an occupied or a vacant slot in the map.

Why the Entry API Exists

Without the Entry API, conditionally inserting or updating a value requires looking up (and hashing/comparing) the key twice—once with contains_key or get, and a second time with insert. Calling map.entry(key) hashes or searches for key once and returns an enum pointing directly to the bucket or B-Tree slot:

pub enum Entry<'a, K: 'a, V: 'a> {
    Occupied(OccupiedEntry<'a, K, V>),
    Vacant(VacantEntry<'a, K, V>),
}

Key Methods on Entry<'a, K, V>

Method Signature Behavior
.or_insert(default) fn or_insert(self, default: V) -> &'a mut V Inserts default if vacant; returns &mut V to the existing or new value
.or_insert_with(f) fn or_insert_with<F: FnOnce() -> V>(self, f: F) -> &'a mut V Evaluates f() lazily only if the entry is vacant
.or_insert_with_key(f) fn or_insert_with_key<F: FnOnce(&K) -> V>(self, f: F) -> &'a mut V Evaluates f(&key) lazily if vacant
.or_default() fn or_default(self) -> &'a mut V (where V: Default) Inserts V::default() if vacant and returns &mut V
.and_modify(f) fn and_modify<F: FnOnce(&mut V)>(self, f: F) -> Self Mutates the existing value in place only if occupied before chaining .or_insert(...)

Chaining .and_modify() and .or_insert()

use std::collections::BTreeMap;

#[derive(Debug, Default)]
struct EndpointStats {
    requests: u64,
    errors: u64,
}

fn main() {
    let mut stats: BTreeMap<&str, EndpointStats> = BTreeMap::new();

    let events = [
        ("/api/login", false),
        ("/api/login", true),
        ("/api/orders", false),
        ("/api/login", false),
    ];

    for (path, is_error) in events {
        // `.or_default()` inserts `EndpointStats::default()` on first visit:
        let entry = stats.entry(path).or_default();
        entry.requests += 1;
        if is_error {
            entry.errors += 1;
        }
    }

    for (path, data) in &stats {
        println!("{path:<12} -> {data:?}");
    }
}
/api/login   -> EndpointStats { requests: 3, errors: 1 }
/api/orders  -> EndpointStats { requests: 1, errors: 0 }

Pattern Matching Directly on Entry::Occupied and Entry::Vacant

When you need to remove an entry conditionally (for example, removing a key when its reference count drops to zero), match on Entry::Occupied(mut occ) and call occ.remove():

use std::collections::HashMap;
use std::collections::hash_map::Entry;

fn decrement_or_remove(counts: &mut HashMap<&str, u32>, item: &str) {
    if let Entry::Occupied(mut occ) = counts.entry(item) {
        if *occ.get() > 1 {
            *occ.get_mut() -= 1;
        } else {
            occ.remove(); // Removes the key-value pair in O(1) without re-hashing!
        }
    }
}

fn main() {
    let mut cart = HashMap::from([("apple", 2), ("banana", 1)]);
    decrement_or_remove(&mut cart, "apple");
    decrement_or_remove(&mut cart, "banana");

    println!("Cart after decrement: {cart:?}");
}
Cart after decrement: {"apple": 1}