Option

Store Optional Values in Option

std

Rust has no null. Instead, it represents the presence or absence of a value with the std::option::Option⮳ enum:

enum Option<T> {
  None,
  Some(T),
}

Every std::option::Option⮳ is either std::option::Option::Some⮳ and contains a value, or std::option::Option::None⮳, and does not.

fn main() {
    // `Some` is a variant of the `Option` enum that represents a value.
    let some_number = Some(5);
    println!("{:?}", some_number);

    // `None` is another variant of the `Option` enum
    // that represents the _absence_ of a value.
    let absent_number: Option<i32> = None;
    println!("{:?}", absent_number);
}

Option<T> is similar to the concept of "nullable" types in other languages, but with a crucial difference: the Option type forces you to explicitly handle both the "value present" and "value absent" cases, preventing common null pointer errors.

Use Option with match, if let, or while let

Option<T> is often used with match⮳, if let, or while let:

/// This function simulates baking a cake.
///
/// It takes an optional `sprinkles` argument, which determines
/// whether or not to add sprinkles to the cake.
///
/// Returns a string describing the cake.
fn bake_cake(sprinkles: Option<&str>) -> String {
    let mut cake = String::from("A delicious cake...");

    // Add required ingredients.

    // Handle optional sprinkles.
    // `if let` binds the `Option` to a `Some(...)` pattern.
    // If and only if there is a value, `sprinkle_choice` is assigned the
    // content of the option.
    if let Some(sprinkle_choice) = sprinkles {
        cake.push_str(
            // `sprinkle_choice` is never "null" / unassigned.
            format!(" with a sprinkle of {}", sprinkle_choice).as_str(),
        );
    } else {
        // Optional fallback behavior if `sprinkles` is `None`.
        cake.push_str(" ready for your decorating touch!");
    }
    cake
}

fn main() {
    println!("{}", bake_cake(Some("rainbow nonpareils")));
    // The absence of a value is noted explicitly with `None`.
    println!("{}", bake_cake(None));
}
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
    if denominator == 0.0 {
        None
    } else {
        Some(numerator / denominator)
    }
}

fn main() {
    let result1 = divide(10.0, 2.0);
    match result1 {
        // Handles the `Some` case.
        Some(value) => println!("Result: {}", value),
        // Handles the `None` case.
        None => println!("Cannot divide by zero!"),
    }
}

Implement the "Early Return" Pattern with the ? Operator

std

"Early Exit" (also called "Early Return") is the technique of immediately returning from a function (or exiting a loop) as soon as an invalid or exceptional condition is detected. For this purpose, Rust offers the ? operator, which is essentially a shortcut for a match expression:

fn func() -> Option<...> {
   let maybe_val: Option<...> = ...;
   let val = match maybe_val {
      Some(v) => v,
      _ => return None,  // Early exit.
   }
 // ...
 Some(val)
}

In the example below, when s.chars().next()? encounters None, the function is exited early (returning None), guarding the subsequent uppercase conversion logic from operating on an empty string.

This pattern removes the need for explicit, convoluted match or if else statements, making the "Happy Path" clearer to the reader.

//! The `?` operator shines when you have a sequence of operations, each of
//! which might fail. It provides an early exit mechanism for functions that
//! return `Option`. If any intermediate step returns `None`, the function
//! immediately returns `None`.
//!
//! `?` works as well within functions that return `Result`.

/// Attempt to retrieve the first character from the input string `s`.
fn get_first_char_uppercase(input: Option<String>) -> Option<char> {
    // If `input` is `None`, the `?` operator will return `None` from this
    // function. If `input` is `Some`, it unwraps `input` and assigns it to
    // `s`.
    let s = input?;

    // If the string is empty, `chars().next()` returns `None`, indicating no
    // character is found. The `?` operator then immediately returns `None`
    // from the function, effectively propagating the absence of a character
    // up the call stack.
    let first_char = s.chars().next()?;

    // Convert the character to its uppercase equivalent.
    // `to_ascii_uppercase()` returns a char.
    Some(first_char.to_ascii_uppercase())
}

fn main() {
    // `Option` contains a `String` and it's not empty.
    let s1 = Some("hello".to_string());
    match get_first_char_uppercase(s1) {
        Some(c) => println!("First uppercase char: {}", c), // Output: H.
        None => println!("Could not process string."),
    }

    // `Option` contains an empty `String` (or is `None`).
    let s2 = Some("".to_string());
    match get_first_char_uppercase(s2) {
        Some(c) => println!("First uppercase char: {}", c),
        None => println!("Could not process string."), /* Output: Could not
                                                        * process string. */
    }
}

Common Option Methods

std

The following table provides an overview of the most commonly used Option methods:

CombinatorDescriptionExample Use Case
is_someReturns true if the Option is Some, false otherwise.Checking if a value exists without unwrapping it.
is_noneReturns true if the Option is None, false otherwise.Checking if a value is absent.
mapTransforms the Some value into a new Option containing the result of applying a function f. If self is None, it remains None.Converting a Some(String) to Some(usize) (e.g., length).
and_thenChains operations that themselves return an Option. If self is Some(v), applies f to v and returns the resulting Option. If self is None, returns None. Prevents Option<Option<T>> (nesting).Chaining database lookups where each step might return None (e.g., get_user_id.and_then(get_user_profile)).
orReturns self if it is Some. Otherwise, returns other. Useful for providing a fallback Option.Providing a default configuration if the primary configuration is missing.
or_elseSimilar to or, but the fallback Option is computed by a closure f only if self is None. Useful for expensive fallback computations.Fetching data from a backup source only if the primary source is unavailable.
unwrap_orReturns the contained Some value, or a provided default value if self is None.Getting a user-provided name or defaulting to "Guest".
unwrap_or_elseReturns the contained Some value, or computes it from a closure f if self is None. Useful when the default value is expensive to compute.Generating a new unique ID only if an existing one isn't found.
ok_orConverts Option<T> into a Result<T, E>. If self is Some(v), returns Ok(v). If self is None, returns Err(err).Converting an Option of a parsed number to a Result indicating success or a parsing error.
ok_or_elseSimilar to ok_or, but the error value E is computed by a closure err_fn only if self is None.Converting None to a detailed error message that needs to be generated.
flattenRemoves one level of nesting from an Option of an Option (i.e., Option<Option<T>> becomes Option<T>).After a map operation that returns an Option, use flatten to simplify the result.
filterReturns Some(t) if self is Some(t) AND the predicate function returns true. Otherwise, returns None.Checking if a Some(age) is old enough to vote (`age.filter(
zipIf both self and other are Some, returns a Some containing a tuple of their values. Otherwise, returns None.Combining Option<FirstName> and Option<LastName> into Option<(String, String)>.

Extract the Value Contained in an Option with unwrap* and expect

std

The following methods extract the contained value in an std::option::Option when it is the Some variant. If the std::option::Option⮳ is None:

#![allow(clippy::unnecessary_literal_unwrap)]
//! Demonstrates the use of `unwrap`, `expect`, `unwrap_or`, and
//! `unwrap_or_else` with `Option`.

/// `unwrap()` and `expect()` retrieve the inner value of a `Option`.
/// BEWARE: if the `Option` is `None`, they will cause your program to panic.
///
/// You should only use `unwrap()` or `expect()` when you are absolutely certain
/// that the `Option` will be `Some`.
fn unwrap_expect() {
    let x = Some(10);
    let _value = x.unwrap(); // `value` is 10.

    // This will panic!
    // let y: Option<i32> = None;
    // let _another_value = y.unwrap();

    // `unwrap()` panics with a default message.
    // `expect("custom message")` panics with your specified custom message,
    // which is useful for debugging.
    // If calling `expect` is always safe, explain why in the message.
    let z = Some("A string");
    let _value =
        z.expect("Can't panic: the `Option` is guaranteed to be `Some`.");
}

fn unwrap_or_unwrap_or_else() {
    let config_setting = Some(100);
    let _ = config_setting.unwrap_or(50); // 100.

    let config_setting: Option<u32> = None;
    let _ = config_setting.unwrap_or(50); // 50.

    let config_setting: Option<u32> = None;
    let _ = config_setting.unwrap_or_default(); // 0.

    // `unwrap_or_else` is useful when the default value is expensive to
    // compute, and you only want to compute it if the `Option` is `None`.
    let config_setting: Option<u32> = None;
    let _ = config_setting.unwrap_or_else(|| {
        println!("Calculating expensive default...");
        75
    }); // 75.
}

fn main() {
    unwrap_expect();
    unwrap_or_unwrap_or_else();
}

Transform Values Contained within an Option

std

The following example demonstrates the use of the map, and_then combinators and the unwrap_or_else method. The and_then and *or_else methods take a closure as input, and only evaluate the closure when they need to produce a new value.

use std::fs;

/// Reads a file and returns its contents as `Some(String)` containing the
/// trimmed, lower-case file contents if successful, otherwise `None`.
fn read_file(filename: &str) -> Option<String> {
    fs::read_to_string(filename)
        // Convert a `Result` to `Option`.
        .ok()
        // `and_then` applies a function to the wrapped value if it's `Some`.
        .and_then(|contents| Some(contents.trim().to_string()))
        // `map` is similar, but the closure does not need to return an `Option`:
        .map(|s| s.to_lowercase())
}

fn main() {
    let contents_maybe = read_file("temp/poem.txt");
    // Provide a default with `unwrap_or_else` or `unwrap_or_default`.
    let contents = contents_maybe.unwrap_or_else(String::new);
    println!("{}", contents);
}

Convert &Option<T> into Option<&T> or Similar

std

When working with references to Option, use:

See also the AsRef chapter.

fn as_ref() {
    let text: Option<String> = Some("Hello, world!".to_string());
    // First, cast `Option<String>` to `Option<&String>` with `as_ref`,
    // then consume that with `map`, leaving `text` on the stack.
    let text_length: Option<usize> = text.as_ref().map(|s| s.len());
    println!(
        "We still can print `text`: {text:?}. Length: {:?}",
        text_length
    );
}

fn as_mut() {
    let mut x = Some(2);
    match x.as_mut() {
        Some(v) => {
            *v = 42;
            println!("x is Some({v})");
        }
        None => {
            println!("x is None");
        }
    }
    assert_eq!(x, Some(42));
}

fn main() {
    as_ref();
    as_mut();
}

See Also

  • The ShowOption⮳ crate provides extension methods and macros for formatting Options.

Related Topics

  • Iterators.
  • Match.
  • Result.
  • Rust Patterns.