Option
Store Optional Values in Option
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
"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
The following table provides an overview of the most commonly used Option
↗ methods:
Combinator | Description | Example Use Case |
---|---|---|
is_some ↗ | Returns true if the Option is Some , false otherwise. | Checking if a value exists without unwrapping it. |
is_none ↗ | Returns true if the Option is None , false otherwise. | Checking if a value is absent. |
map ↗ | Transforms 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_then ↗ | Chains 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) ). |
or ↗ | Returns 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_else ↗ | Similar 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_or ↗ | Returns the contained Some value, or a provided default value if self is None . | Getting a user-provided name or defaulting to "Guest". |
unwrap_or_else | Returns 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_or ↗ | Converts 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_else ↗ | Similar 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. |
flatten ↗ | Removes 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. |
filter ↗ | Returns 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( |
zip ↗ | If 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
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
:
std::option::Option::unwrap
↗ panics with a generic message.std::option::Option::expect
↗ panics with a provided custom message.std::option::Option::unwrap_or
↗ returns the provided default value.std::option::Option::unwrap_or_default
↗ returns the default value of the type T (which must implement thestd::default::Default
↗ trait).std::option::Option::unwrap_or_else
↗ returns the result of evaluating the provided closure.
#![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
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
When working with references to Option
, use:
std::option::Option::as_ref
andstd::option::Option::as_mut
to convert from&Option<T>
toOption<&T>
andOption<&mut T>
.std::option::Option::as_deref
↗ to convert from&Option<T>
toOption<&T::Target>
.std::option::Option::as_deref_mut
↗ to convert from&mut Option<T>
toOption<&mut T::Target>
.
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.