Expand description
Optional values.
Type Option
represents an optional value: every Option
is either Some
and contains a value, or None
, and
does not. Option
types are very common in Rust code, as
they have a number of uses:
- Initial values
- Return values for functions that are not defined over their entire input range (partial functions)
- Return value for otherwise reporting simple errors, where
None
is returned on error - Optional struct fields
- Struct fields that can be loaned or “taken”
- Optional function arguments
- Nullable pointers
- Swapping things out of difficult situations
Option
s are commonly paired with pattern matching to query the presence
of a value and take action, always accounting for the None
case.
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}
// The return value of the function is an option
let result = divide(2.0, 3.0);
// Pattern match to retrieve the value
match result {
// The division was valid
Some(x) => println!("Result: {x}"),
// The division was invalid
None => println!("Cannot divide by 0"),
}
RunOptions and pointers (“nullable” pointers)
Rust’s pointer types must always point to a valid location; there are
no “null” references. Instead, Rust has optional pointers, like
the optional owned box, Option<Box<T>>
.
The following example uses Option
to create an optional box of
i32
. Notice that in order to use the inner i32
value, the
check_optional
function first needs to use pattern matching to
determine whether the box has a value (i.e., it is Some(...)
) or
not (None
).
let optional = None;
check_optional(optional);
let optional = Some(Box::new(9000));
check_optional(optional);
fn check_optional(optional: Option<Box<i32>>) {
match optional {
Some(p) => println!("has value {p}"),
None => println!("has no value"),
}
}
RunThe question mark operator, ?
Similar to the Result
type, when writing code that calls many functions that return the
Option
type, handling Some
/None
can be tedious. The question mark
operator, ?
, hides some of the boilerplate of propagating values
up the call stack.
It replaces this:
fn add_last_numbers(stack: &mut Vec<i32>) -> Option<i32> {
let a = stack.pop();
let b = stack.pop();
match (a, b) {
(Some(x), Some(y)) => Some(x + y),
_ => None,
}
}
RunWith this:
fn add_last_numbers(stack: &mut Vec<i32>) -> Option<i32> {
Some(stack.pop()? + stack.pop()?)
}
RunIt’s much nicer!
Ending the expression with ?
will result in the Some
’s unwrapped value, unless the
result is None
, in which case None
is returned early from the enclosing function.
?
can be used in functions that return Option
because of the
early return of None
that it provides.
Representation
Rust guarantees to optimize the following types T
such that
Option<T>
has the same size as T
:
Box<U>
&U
&mut U
fn
,extern "C" fn
1num::NonZero*
ptr::NonNull<U>
#[repr(transparent)]
struct around one of the types in this list.
This is called the “null pointer optimization” or NPO.
It is further guaranteed that, for the cases above, one can
mem::transmute
from all valid values of T
to Option<T>
and
from Some::<T>(_)
to T
(but transmuting None::<T>
to T
is undefined behaviour).
Method overview
In addition to working with pattern matching, Option
provides a wide
variety of different methods.
Querying the variant
The is_some
and is_none
methods return true
if the Option
is Some
or None
, respectively.
Adapters for working with references
as_ref
converts from&Option<T>
toOption<&T>
as_mut
converts from&mut Option<T>
toOption<&mut T>
as_deref
converts from&Option<T>
toOption<&T::Target>
as_deref_mut
converts from&mut Option<T>
toOption<&mut T::Target>
as_pin_ref
converts fromPin<&Option<T>>
toOption<Pin<&T>>
as_pin_mut
converts fromPin<&mut Option<T>>
toOption<Pin<&mut T>>
Extracting the contained value
These methods extract the contained value in an Option<T>
when it
is the Some
variant. If the Option
is None
:
expect
panics with a provided custom messageunwrap
panics with a generic messageunwrap_or
returns the provided default valueunwrap_or_default
returns the default value of the typeT
(which must implement theDefault
trait)unwrap_or_else
returns the result of evaluating the provided function
Transforming contained values
These methods transform Option
to Result
:
ok_or
transformsSome(v)
toOk(v)
, andNone
toErr(err)
using the provided defaulterr
valueok_or_else
transformsSome(v)
toOk(v)
, andNone
to a value ofErr
using the provided functiontranspose
transposes anOption
of aResult
into aResult
of anOption
These methods transform the Some
variant:
filter
calls the provided predicate function on the contained valuet
if theOption
isSome(t)
, and returnsSome(t)
if the function returnstrue
; otherwise, returnsNone
flatten
removes one level of nesting from anOption<Option<T>>
map
transformsOption<T>
toOption<U>
by applying the provided function to the contained value ofSome
and leavingNone
values unchanged
These methods transform Option<T>
to a value of a possibly
different type U
:
map_or
applies the provided function to the contained value ofSome
, or returns the provided default value if theOption
isNone
map_or_else
applies the provided function to the contained value ofSome
, or returns the result of evaluating the provided fallback function if theOption
isNone
These methods combine the Some
variants of two Option
values:
zip
returnsSome((s, o))
ifself
isSome(s)
and the providedOption
value isSome(o)
; otherwise, returnsNone
zip_with
calls the provided functionf
and returnsSome(f(s, o))
ifself
isSome(s)
and the providedOption
value isSome(o)
; otherwise, returnsNone
Boolean operators
These methods treat the Option
as a boolean value, where Some
acts like true
and None
acts like false
. There are two
categories of these methods: ones that take an Option
as input, and
ones that take a function as input (to be lazily evaluated).
The and
, or
, and xor
methods take another Option
as
input, and produce an Option
as output. Only the and
method can
produce an Option<U>
value having a different inner type U
than
Option<T>
.
method | self | input | output |
---|---|---|---|
and | None | (ignored) | None |
and | Some(x) | None | None |
and | Some(x) | Some(y) | Some(y) |
or | None | None | None |
or | None | Some(y) | Some(y) |
or | Some(x) | (ignored) | Some(x) |
xor | None | None | None |
xor | None | Some(y) | Some(y) |
xor | Some(x) | None | Some(x) |
xor | Some(x) | Some(y) | None |
The and_then
and or_else
methods take a function as input, and
only evaluate the function when they need to produce a new value. Only
the and_then
method can produce an Option<U>
value having a
different inner type U
than Option<T>
.
method | self | function input | function result | output |
---|---|---|---|---|
and_then | None | (not provided) | (not evaluated) | None |
and_then | Some(x) | x | None | None |
and_then | Some(x) | x | Some(y) | Some(y) |
or_else | None | (not provided) | None | None |
or_else | None | (not provided) | Some(y) | Some(y) |
or_else | Some(x) | (not provided) | (not evaluated) | Some(x) |
This is an example of using methods like and_then
and or
in a
pipeline of method calls. Early stages of the pipeline pass failure
values (None
) through unchanged, and continue processing on
success values (Some
). Toward the end, or
substitutes an error
message if it receives None
.
let mut bt = BTreeMap::new();
bt.insert(20u8, "foo");
bt.insert(42u8, "bar");
let res = [0u8, 1, 11, 200, 22]
.into_iter()
.map(|x| {
// `checked_sub()` returns `None` on error
x.checked_sub(1)
// same with `checked_mul()`
.and_then(|x| x.checked_mul(2))
// `BTreeMap::get` returns `None` on error
.and_then(|x| bt.get(&x))
// Substitute an error message if we have `None` so far
.or(Some(&"error!"))
.copied()
// Won't panic because we unconditionally used `Some` above
.unwrap()
})
.collect::<Vec<_>>();
assert_eq!(res, ["error!", "error!", "foo", "error!", "bar"]);
RunComparison operators
If T
implements PartialOrd
then Option<T>
will derive its
PartialOrd
implementation. With this order, None
compares as
less than any Some
, and two Some
compare the same way as their
contained values would in T
. If T
also implements
Ord
, then so does Option<T>
.
assert!(None < Some(0));
assert!(Some(0) < Some(1));
RunIterating over Option
An Option
can be iterated over. This can be helpful if you need an
iterator that is conditionally empty. The iterator will either produce
a single value (when the Option
is Some
), or produce no values
(when the Option
is None
). For example, into_iter
acts like
once(v)
if the Option
is Some(v)
, and like empty()
if
the Option
is None
.
Iterators over Option<T>
come in three types:
into_iter
consumes theOption
and produces the contained valueiter
produces an immutable reference of type&T
to the contained valueiter_mut
produces a mutable reference of type&mut T
to the contained value
An iterator over Option
can be useful when chaining iterators, for
example, to conditionally insert items. (It’s not always necessary to
explicitly call an iterator constructor: many Iterator
methods that
accept other iterators will also accept iterable types that implement
IntoIterator
, which includes Option
.)
let yep = Some(42);
let nope = None;
// chain() already calls into_iter(), so we don't have to do so
let nums: Vec<i32> = (0..4).chain(yep).chain(4..8).collect();
assert_eq!(nums, [0, 1, 2, 3, 42, 4, 5, 6, 7]);
let nums: Vec<i32> = (0..4).chain(nope).chain(4..8).collect();
assert_eq!(nums, [0, 1, 2, 3, 4, 5, 6, 7]);
RunOne reason to chain iterators in this way is that a function returning
impl Iterator
must have all possible return values be of the same
concrete type. Chaining an iterated Option
can help with that.
fn make_iter(do_insert: bool) -> impl Iterator<Item = i32> {
// Explicit returns to illustrate return types matching
match do_insert {
true => return (0..4).chain(Some(42)).chain(4..8),
false => return (0..4).chain(None).chain(4..8),
}
}
println!("{:?}", make_iter(true).collect::<Vec<_>>());
println!("{:?}", make_iter(false).collect::<Vec<_>>());
RunIf we try to do the same thing, but using once()
and empty()
,
we can’t return impl Iterator
anymore because the concrete types of
the return values differ.
// This won't compile because all possible returns from the function
// must have the same concrete type.
fn make_iter(do_insert: bool) -> impl Iterator<Item = i32> {
// Explicit returns to illustrate return types not matching
match do_insert {
true => return (0..4).chain(once(42)).chain(4..8),
false => return (0..4).chain(empty()).chain(4..8),
}
}
RunCollecting into Option
Option
implements the FromIterator
trait,
which allows an iterator over Option
values to be collected into an
Option
of a collection of each contained value of the original
Option
values, or None
if any of the elements was None
.
let v = [Some(2), Some(4), None, Some(8)];
let res: Option<Vec<_>> = v.into_iter().collect();
assert_eq!(res, None);
let v = [Some(2), Some(4), Some(8)];
let res: Option<Vec<_>> = v.into_iter().collect();
assert_eq!(res, Some(vec![2, 4, 8]));
RunOption
also implements the Product
and
Sum
traits, allowing an iterator over Option
values
to provide the product
and
sum
methods.
let v = [None, Some(1), Some(2), Some(3)];
let res: Option<i32> = v.into_iter().sum();
assert_eq!(res, None);
let v = [Some(1), Some(2), Some(21)];
let res: Option<i32> = v.into_iter().product();
assert_eq!(res, Some(42));
RunModifying an Option
in-place
These methods return a mutable reference to the contained value of an
Option<T>
:
insert
inserts a value, dropping any old contentsget_or_insert
gets the current value, inserting a provided default value if it isNone
get_or_insert_default
gets the current value, inserting the default value of typeT
(which must implementDefault
) if it isNone
get_or_insert_with
gets the current value, inserting a default computed by the provided function if it isNone
These methods transfer ownership of the contained value of an
Option
:
take
takes ownership of the contained value of anOption
, if any, replacing theOption
withNone
replace
takes ownership of the contained value of anOption
, if any, replacing theOption
with aSome
containing the provided value
Examples
Basic pattern matching on Option
:
let msg = Some("howdy");
// Take a reference to the contained string
if let Some(m) = &msg {
println!("{}", *m);
}
// Remove the contained string, destroying the Option
let unwrapped_msg = msg.unwrap_or("default message");
RunInitialize a result to None
before a loop:
enum Kingdom { Plant(u32, &'static str), Animal(u32, &'static str) }
// A list of data to search through.
let all_the_big_things = [
Kingdom::Plant(250, "redwood"),
Kingdom::Plant(230, "noble fir"),
Kingdom::Plant(229, "sugar pine"),
Kingdom::Animal(25, "blue whale"),
Kingdom::Animal(19, "fin whale"),
Kingdom::Animal(15, "north pacific right whale"),
];
// We're going to search for the name of the biggest animal,
// but to start with we've just got `None`.
let mut name_of_biggest_animal = None;
let mut size_of_biggest_animal = 0;
for big_thing in &all_the_big_things {
match *big_thing {
Kingdom::Animal(size, name) if size > size_of_biggest_animal => {
// Now we've found the name of some big animal
size_of_biggest_animal = size;
name_of_biggest_animal = Some(name);
}
Kingdom::Animal(..) | Kingdom::Plant(..) => ()
}
}
match name_of_biggest_animal {
Some(name) => println!("the biggest animal is {name}"),
None => println!("there are no animals :("),
}
Runthis remains true for any other ABI:
extern "abi" fn
(e.g.,extern "system" fn
) ↩
Structs
Enums
- The
Option
type. See the module level documentation for more.
Traits
- SpecOptionPartialEqExperimentalThis specialization trait is a workaround for LLVM not currently (2023-01) being able to optimize this itself, even though Alive confirms that it would be legal to do so: https://github.com/llvm/llvm-project/issues/52622