#226 Jun 27, 2026

226. unwrap_or_default — Stop Spelling Out the Empty Value

Writing .unwrap_or(0) or .unwrap_or_else(String::new) to fall back to an empty value? If the type already has a Default, unwrap_or_default says it for you.

The fallback you keep typing out

You pull a value out of an Option, and the “missing” case is just the type’s natural zero: 0 for a number, "" for a string, [] for a vec. So you spell it out:

1
2
3
4
5
6
7
let count: Option<u32> = None;
let n = count.unwrap_or(0);
assert_eq!(n, 0);

let name: Option<String> = None;
let s = name.unwrap_or_else(String::new);
assert_eq!(s, "");

Every one of those fallbacks is just Default::default(). unwrap_or_default reaches for it directly — no literal to pick, no closure to write:

1
2
3
4
5
6
7
8
let count: Option<u32> = None;
assert_eq!(count.unwrap_or_default(), 0);

let name: Option<String> = None;
assert_eq!(name.unwrap_or_default(), "");

let items: Option<Vec<i32>> = None;
assert_eq!(items.unwrap_or_default(), Vec::<i32>::new());

When Some, you get the value untouched; when None, you get T::default().

Where it shines: map lookups

Counting with a HashMap is the classic case — a missing key should read as zero:

1
2
3
4
5
6
7
8
9
use std::collections::HashMap;

let mut counts: HashMap<&str, u32> = HashMap::new();
counts.insert("hits", 3);

let hits = counts.get("hits").copied().unwrap_or_default();
let misses = counts.get("misses").copied().unwrap_or_default();
assert_eq!(hits, 3);
assert_eq!(misses, 0);

No .unwrap_or(0) sprinkled at every call site, and if the value type changes, the default follows along automatically.

It works on Result too

Result::unwrap_or_default discards the Err and hands back the default — handy when a parse failure should just mean “nothing”:

1
2
3
4
let good = "42".parse::<i32>().unwrap_or_default();
let bad = "oops".parse::<i32>().unwrap_or_default();
assert_eq!(good, 42);
assert_eq!(bad, 0);

And on your own types

Derive Default and the same trick works for your structs — the fallback stays in one place instead of scattered across the codebase:

1
2
3
4
5
6
7
8
#[derive(Default, Debug, PartialEq)]
struct Config {
    retries: u32,
    verbose: bool,
}

let cfg: Option<Config> = None;
assert_eq!(cfg.unwrap_or_default(), Config { retries: 0, verbose: false });

Reach for unwrap_or_default whenever the fallback is the empty value — let the type decide what empty means.

← Previous 225. Option::map_or — Transform-or-Default in One Call, Skip the match Next → 227. trim_matches — Strip the Same Char Off Both Ends, However Many There Are