#181 Jun 4, 2026

181. Option::get_or_insert_with — Lazy Default That Returns &mut

You have an Option<Vec<T>> field and want to push to it. If it’s None, allocate first; if it’s Some, just push. get_or_insert_with does both in one call — and hands you back a &mut so you can use it on the same line.

The dance you don’t have to do

The naïve version checks, assigns, then unwraps:

1
2
3
4
5
6
7
8
let mut tags: Option<Vec<String>> = None;

if tags.is_none() {
    tags = Some(Vec::new());
}
tags.as_mut().unwrap().push("verbose".into());

assert_eq!(tags, Some(vec!["verbose".to_string()]));

Three lines, an unwrap, and you re-borrow the Option twice. get_or_insert_with collapses it:

1
2
3
4
5
let mut tags: Option<Vec<String>> = None;

tags.get_or_insert_with(Vec::new).push("clean".into());

assert_eq!(tags, Some(vec!["clean".to_string()]));

It initializes the Option to Some(f()) if and only if it was None, and returns &mut T to the inner value either way. No unwrap, no double-check.

The closure only runs when needed

That’s the whole point of the _with suffix: the default is lazy. If the value’s already there, your closure never fires, which matters when the default is expensive or has side effects:

1
2
3
4
5
6
7
8
let mut cache: Option<Vec<u8>> = Some(vec![1, 2, 3]);

let buf = cache.get_or_insert_with(|| {
    panic!("would allocate a huge buffer");
});
buf.push(4);

assert_eq!(cache, Some(vec![1, 2, 3, 4]));

If your default is cheap (a String::new(), a 0u32), use the eager sibling get_or_insert and skip the closure:

1
2
3
let mut log: Option<String> = None;
log.get_or_insert(String::new()).push_str("hi");
assert_eq!(log.as_deref(), Some("hi"));

Why the &mut return matters

get_or_insert_with returns &mut T, not T or Option<T>. That lets you keep chaining — push, mutate, hand to another function — without ever re-borrowing the Option:

1
2
3
4
5
6
let mut counters: Option<Vec<u32>> = None;

counters.get_or_insert_with(Vec::new).extend([1, 2, 3]);
counters.get_or_insert_with(Vec::new).push(4);

assert_eq!(counters, Some(vec![1, 2, 3, 4]));

The classic case is builders and config structs with Option<Vec<_>> fields that should only allocate when the caller actually adds something. One line per add, no upfront Some(Vec::new()), no unwrap.

Stable since Rust 1.20.

← Previous 180. Option::unzip — Split an Optional Pair Into a Pair of Options