Error Handling

unwrap_or() and Related Methods

unwrap_or(), unwrap_or_else(), and unwrap_or_default() are non-panicking extraction methods on Option<T> and Result<T, E>. They return the inner value T when the receiver is Some(T) or Ok(T), and substitute a fallback value of type T when the receiver is None or Err(E).

Fallback Extraction Methods Compared

Option<T> / Result<T, E>
  ├── Some(v) / Ok(v)  ──> returns v
  └── None    / Err(e) ──┬── unwrap_or(fallback)       --> returns `fallback` (evaluated eagerly)
                         ├── unwrap_or_else(f)         --> calls `f()` or `f(e)` (evaluated lazily)
                         └── unwrap_or_default()       --> returns `T::default()`
Method Evaluation Timing Bounds Required Best Used When
unwrap_or(default) Eager: default is evaluated before the method runs None default is a cheap literal or already-computed variable (0, "", true)
unwrap_or_else(f) Lazy: closure f runs only on None or Err(e) FnOnce() -> T (Option) or FnOnce(E) -> T (Result) Computing the fallback allocates memory, does I/O, or logs the error e
unwrap_or_default() Lazy: T::default() runs only on None or Err(e) T: Default The natural fallback is T's zero/empty value (0, "", Vec::new())

Constant Fallbacks with unwrap_or()

Use unwrap_or(val) when the fallback is a constant or trivial expression that does not allocate:

fn main() {
    let custom_port: Option<u16> = None;
    let configured_port: Option<u16> = Some(9000);

    println!("Port A: {}", custom_port.unwrap_or(8080));
    println!("Port B: {}", configured_port.unwrap_or(8080));

    let parsed: Result<i32, _> = "invalid".parse();
    println!("Parsed count: {}", parsed.unwrap_or(-1));
}
Port A: 8080
Port B: 9000
Parsed count: -1

Lazy Fallbacks with unwrap_or_else()

Because function arguments in Rust are evaluated before the function is called, passing String::from("fallback") to unwrap_or(...) allocates a String even when the Option is Some. Passing a closure to unwrap_or_else(...) avoids the work on the success path and gives Result access to the error E:

fn expensive_default(reason: &str) -> String {
    println!("  [computing fallback because of: {reason}]");
    String::from("guest_session")
}

fn main() {
    let active: Result<String, &str> = Ok(String::from("admin_session"));
    let expired: Result<String, &str> = Err("token expired");

    let s1 = active.unwrap_or_else(expensive_default);
    println!("s1 = {s1}");

    let s2 = expired.unwrap_or_else(expensive_default);
    println!("s2 = {s2}");
}
s1 = admin_session
  [computing fallback because of: token expired]
s2 = guest_session

Type-Default Fallbacks with unwrap_or_default()

For any type T that implements std::default::Default (numbers default to 0, bool to false, String to "", Vec<U> to [], Option<U> to None), unwrap_or_default() returns T::default() on None or Err:

fn main() {
    let missing_name: Option<String> = None;
    let bad_number: Result<u32, _> = "NaN".parse();
    let missing_items: Option<Vec<i32>> = None;

    println!("name:  {:?}", missing_name.unwrap_or_default());
    println!("num:   {}", bad_number.unwrap_or_default());
    println!("items: {:?}", missing_items.unwrap_or_default());
}
name:  ""
num:   0
items: []