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.