#166 May 27, 2026

166. Entry::and_modify — Update If Present, Insert If Not, in One Chain

*map.entry(k).or_insert(0) += 1 is the classic Rust counter. The moment the “first time” branch needs to look different from the “next time” branch, that pattern stops fitting — and and_modify slots in.

HashMap::entry(k) returns an Entry enum pointing at the slot for k, occupied or vacant. The famous methods are or_insert(default) and or_insert_with(|| ...). Quieter but often nicer is and_modify: it runs a closure on the value when the key is already there, and does nothing when it isn’t. Chain it with or_insert and you get update if present, insert if not in one expression — with a single lookup.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use std::collections::HashMap;

let mut counts: HashMap<&str, u32> = HashMap::new();

for word in ["rust", "iron", "rust", "rust", "iron"] {
    counts
        .entry(word)
        .and_modify(|n| *n += 1)
        .or_insert(1);
}

assert_eq!(counts["rust"], 3);
assert_eq!(counts["iron"], 2);

For pure counters that’s a tie with *entry.or_insert(0) += 1. The shape really pays off when the two branches store different data. Imagine grouping events by user, where the first event records the user’s name and later events only bump a counter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
use std::collections::HashMap;

#[derive(Debug, PartialEq)]
struct UserStats { name: String, visits: u32 }

let events = [(1u32, "alice"), (2, "bob"), (1, "alice")];
let mut stats: HashMap<u32, UserStats> = HashMap::new();

for (id, name) in events {
    stats
        .entry(id)
        .and_modify(|s| s.visits += 1)
        .or_insert(UserStats { name: name.into(), visits: 1 });
}

assert_eq!(stats[&1], UserStats { name: "alice".into(), visits: 2 });
assert_eq!(stats[&2], UserStats { name: "bob".into(),   visits: 1 });

and_modify takes &mut V so you mutate in place; or_insert produces the initial V. and_modify also returns the Entry back, which is why the chain works — and which means you can stack several modifications before the final or_insert.

← Previous 165. PhantomData<T> — The Zero-Sized Marker That Pretends to Own a T