Option<T> and Result<T, E> in Depth

unwrap_or_default()

The .unwrap_or_default() method on Option<T> and Result<T, E> extracts the contained Some(v) or Ok(v) value, or returns T::default() (Default::default()) if the value is None or Err(_).

Signatures and Fallback Family Comparison

impl<T: Default> Option<T> {
    pub fn unwrap_or_default(self) -> T;
}

impl<T: Default, E> Result<T, E> {
    pub fn unwrap_or_default(self) -> T;
}
Method Fallback Source Evaluation Panics?
x.unwrap() None — Yes (on None / Err)
x.expect("msg") None — Yes (with custom message)
x.unwrap_or(fallback) Explicit fallback: T argument Eager (fallback always evaluated) No
x.unwrap_or_else(f) Closure f() (FnOnce() -> T or FnOnce(E) -> T) Lazy (runs only on None / Err) No
x.unwrap_or_default() T::default() (T: Default) Lazy (calls T::default() only on None / Err) No

Using unwrap_or_default() on Option<T> and Result<T, E>

Because T::default() is 0 for numbers, false for bool, "" (empty, non-allocating String) for String, and [] (empty, non-allocating Vec) for Vec<T>, .unwrap_or_default() eliminates boilerplate fallback literals:

fn main() {
    // 1. On Option<String> -> returns empty String "" on None:
    let present_name: Option<String> = Some(String::from("Alice"));
    let missing_name: Option<String> = None;

    println!("Present: {:?}", present_name.unwrap_or_default());
    println!("Missing: {:?}", missing_name.unwrap_or_default());

    // 2. On Result<u32, ParseIntError> -> returns 0 on Err:
    let valid_count: u32 = "42".parse().unwrap_or_default();
    let bad_count: u32 = "not_a_number".parse().unwrap_or_default();

    println!("Valid count: {valid_count}, Bad count: {bad_count}");
}
Present: "Alice"
Missing: ""
Valid count: 42, Bad count: 0

Taking Ownership From &mut Option<T> Using Option::take()

A closely related Default-based operation on Option<T> itself is opt.take() (equivalent to std::mem::take(&mut opt) since Option::<T>::default() is None): it moves the inner Some(v) out of a &mut Option<T> and leaves None in its place in $O(1)$ time without requiring T: Default or T: Clone:

struct Worker {
    active_task: Option<String>,
}

impl Worker {
    fn finish_task(&mut self) -> String {
        // Moves `Some(task)` out of `self.active_task` (leaving `None`),
        // or returns `""` via `unwrap_or_default()` if no task was active:
        self.active_task.take().unwrap_or_default()
    }
}

fn main() {
    let mut w = Worker {
        active_task: Some(String::from("reindex_db")),
    };

    println!("1st finish: {:?}", w.finish_task());
    println!("2nd finish: {:?}", w.finish_task());
}
1st finish: "reindex_db"
2nd finish: ""