#225 Jun 27, 2026

225. Option::map_or — Transform-or-Default in One Call, Skip the match

Reaching for a four-line match just to turn an Option into a plain value? map_or does the transform and the fallback in a single call.

The match you keep rewriting

You have an Option, you want a concrete value: apply a function if it’s Some, fall back to a default if it’s None.

1
2
3
4
5
6
7
let name: Option<&str> = Some("ferris");

let len = match name {
    Some(n) => n.len(),
    None => 0,
};
assert_eq!(len, 6);

map_or(default, f) collapses both arms. The first argument is the None fallback, the second is what to do with the value inside Some:

1
2
3
4
5
let name: Option<&str> = Some("ferris");
assert_eq!(name.map_or(0, |n| n.len()), 6);

let missing: Option<&str> = None;
assert_eq!(missing.map_or(0, |n| n.len()), 0);

It beats the common .map(|n| n.len()).unwrap_or(0) too — same result, but no intermediate Option built just to immediately unwrap it.

The catch: the default is eager

map_or takes the default by value, so it’s computed whether or not you need it. With a cheap literal like 0 that’s free. With anything that allocates or does real work, you pay for it even on the Some path:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
fn expensive_default() -> String {
    // imagine a config read, an allocation, a computation...
    "fallback".to_string()
}

let port: Option<u16> = Some(8080);

// ⚠️ expensive_default() runs even though port is Some
let label = port.map_or(expensive_default(), |p| format!("port {p}"));
assert_eq!(label, "port 8080");

When the fallback isn’t free, switch to map_or_else, which takes a closure and only calls it on None:

1
2
3
4
5
6
7
fn expensive_default() -> String {
    "fallback".to_string()
}

let port: Option<u16> = None;
let label = port.map_or_else(|| expensive_default(), |p| format!("port {p}"));
assert_eq!(label, "fallback");

It works on Result too

Result::map_or follows the same shape — the value goes through f, any Err yields the default:

1
2
3
4
5
let parsed = "42".parse::<i32>();
assert_eq!(parsed.map_or(-1, |n| n * 2), 84);

let bad = "oops".parse::<i32>();
assert_eq!(bad.map_or(-1, |n| n * 2), -1);

Use map_or when the default is a cheap literal, map_or_else when it isn’t, and let the match go.

← Previous 224. String::from_utf8_lossy — Returns a Cow, So Valid Bytes Cost Zero Next → 226. unwrap_or_default — Stop Spelling Out the Empty Value