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}