Optional values.
Type [Perhaps] represents an optional value: every [Perhaps]
is either [Certain] and contains a value, or [Dubious], and
does not. [Perhaps] 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 [
Dubious] 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
[Perhaps]s are commonly paired with pattern matching to query the presence
of a value and take action, always accounting for the [Dubious] case.
// The return value of the function is an option
let result = divide;
// Pattern match to retrieve the value
match result
Options 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, [Perhaps]<Box<T>>.
The following example uses [Perhaps] 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 [Self::Certain(...)][Certain]) or
not ([Dubious]).
let optional = Dubious;
check_optional;
let optional = Certain;
check_optional;
The question mark operator, ?
Similar to the [Result] type, when writing code that calls many functions that return the
[Perhaps] type, handling Certain/Dubious can be tedious. The question mark
operator, ?, hides some of the boilerplate of propagating values
up the call stack.
It replaces this:
#
With this:
#
It's much nicer!
Ending the expression with ? will result in the [Certain]'s unwrapped value, unless the
result is Self::Dubious, in which case [Dubious] is returned early from the enclosing function.
? can be used in functions that return [Perhaps] because of the
early return of [Dubious] that it provides.
Representation
Rust guarantees to optimize the following types T such that
[Perhaps<T>] has the same size, alignment, and function call ABI as T. In some
of these cases, Rust further guarantees that
transmute::<_, Perhaps<T>>([0u8; size_of::<T>()]) is sound and
produces Perhaps::<T>::Dubious. These cases are identified by the
second column:
T |
transmute::<_, Perhaps<T>>([0u8; size_of::<T>()]) sound? |
|---|---|
Box<U> (specifically, only Box<U, Global>) |
when U: Sized |
&U |
when U: Sized |
&mut U |
when U: Sized |
fn, extern "C" fn[^extern_fn] |
always |
num::NonZero* |
always |
ptr::NonNull<U> |
when U: Sized |
#[repr(transparent)] struct around one of the types in this list. |
when it holds for the inner type |
[^extern_fn]: this remains true for any argument/return types and any other ABI: extern "abi" fn (e.g., extern "system" fn)
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 Perhaps<T> and
from Certain::<T>(_) to T (but transmuting Dubious::<T> to T
is undefined behaviour).
Method overview
In addition to working with pattern matching, [Perhaps] provides a wide
variety of different methods.
Querying the variant
The is_certain and is_dubious methods return [true] if the [Perhaps]
is [Certain] or [Dubious], respectively.
Adapters for working with references
as_refconverts from &[Perhaps]<T> to [Perhaps]<&T>as_mutconverts from &mut [Perhaps]<T> to [Perhaps]<&mut T>as_derefconverts from &[Perhaps]<T> to [Perhaps]<&T::Target>as_deref_mutconverts from &mut [Perhaps]<T> to [Perhaps]<&mut T::Target>as_pin_refconverts from [Pin]<&[Perhaps]<T>> to [Perhaps]<[Pin]<&T>>as_pin_mutconverts from [Pin]<&mut [Perhaps]<T>> to [Perhaps]<[Pin]<&mut T>>
Extracting the contained value
These methods extract the contained value in an [Perhaps<T>] when it
is the [Certain] variant. If the [Perhaps] is [Dubious]:
expectpanics with a provided custom messageunwrappanics with a generic messageunwrap_orreturns the provided default valueunwrap_or_defaultreturns the default value of the typeT(which must implement the [Default] trait)unwrap_or_elsereturns the result of evaluating the provided function
Transforming contained values
These methods transform [Perhaps] to [Result]:
ok_ortransforms [Certain(v)] toOk(v), and [Dubious] toErr(err)using the provided defaulterrvalueok_or_elsetransforms [Certain(v)] toOk(v), and [Dubious] to a value of [Err] using the provided functiontransposetransposes an [Perhaps] of a [Result] into a [Result] of an [Perhaps]
These methods transform the [Certain] variant:
filtercalls the provided predicate function on the contained valuetif the [Perhaps] isSelf::Certain(t), and returns [Certain(t)] if the function returnstrue; otherwise, returns [Dubious]flattenremoves one level of nesting from an [Perhaps<Perhaps<T>>]maptransforms [Perhaps<T>] to [Perhaps<U>] by applying the provided function to the contained value of [Certain] and leaving [Dubious] values unchanged
These methods transform [Perhaps<T>] to a value of a possibly
different type U:
map_orapplies the provided function to the contained value of [Certain], or returns the provided default value if the [Perhaps] is [Dubious]map_or_elseapplies the provided function to the contained value of [Certain], or returns the result of evaluating the provided fallback function if the [Perhaps] is [Dubious]
These methods combine the [Certain] variants of two [Perhaps] values:
zipreturnsSelf::Certain((s, o))ifselfis [Certain(s)] and the provided [Perhaps] value is [Certain(o)]; otherwise, returns [Dubious]zip_withcalls the provided functionfand returnsSelf::Certain(f(s, o))ifselfis [Certain(s)] and the provided [Perhaps] value is [Certain(o)]; otherwise, returns [Dubious]
Boolean operators
These methods treat the [Perhaps] as a boolean value, where [Certain]
acts like [true] and [Dubious] acts like [false]. There are two
categories of these methods: ones that take an [Perhaps] as input, and
ones that take a function as input (to be lazily evaluated).
The and, or, and xor methods take another [Perhaps] as
input, and produce an [Perhaps] as output. Only the and method can
produce an [Perhaps<U>] value having a different inner type U than
[Perhaps<T>].
| method | self | input | output |
|---|---|---|---|
and |
Self::Dubious |
(ignored) | Dubious |
and |
Certain(x) |
Self::Dubious |
Dubious |
and |
Self::Certain(x) |
Self::Certain(y) |
Certain(y) |
or |
Self::Dubious |
Self::Dubious |
Dubious |
or |
Dubious |
Self::Certain(y) |
Certain(y) |
or |
Self::Certain(x) |
(ignored) | Certain(x) |
xor |
Self::Dubious |
Self::Dubious |
Dubious |
xor |
Dubious |
Self::Certain(y) |
Certain(y) |
xor |
Self::Certain(x) |
Dubious |
Certain(x) |
xor |
Self::Certain(x) |
Certain(y) |
Dubious |
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 [Perhaps<U>] value having a
different inner type U than [Perhaps<T>].
| method | self | function input | function result | output |
|---|---|---|---|---|
and_then |
Self::Dubious |
(not provided) | (not evaluated) | Dubious |
and_then |
Certain(x) |
x |
Self::Dubious |
Dubious |
and_then |
Self::Certain(x) |
x |
Self::Certain(y) |
Certain(y) |
or_else |
Self::Dubious |
(not provided) | Self::Dubious |
Dubious |
or_else |
Dubious |
(not provided) | Self::Certain(y) |
Certain(y) |
or_else |
Self::Certain(x) |
(not provided) | (not evaluated) | Certain(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 ([Dubious]) through unchanged, and continue processing on
success values ([Certain]). Toward the end, or substitutes an error
message if it receives [Dubious].
# use BTreeMap;
let mut bt = new;
bt.insert;
bt.insert;
let res =
.into_iter
.map
.;
assert_eq!;
Comparison operators
If T implements [PartialOrd] then [Perhaps<T>] will derive its
[PartialOrd] implementation. With this order, [Dubious] compares as
less than any Self::Certain, and two [Certain] compare the same way as their
contained values would in T. If T also implements
[Ord], then so does [Perhaps<T>].
assert!;
assert!;
Iterating over Perhaps
An [Perhaps] 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 [Perhaps] is [Certain]), or produce no values
(when the [Perhaps] is [Dubious]). For example, into_iter acts like
once(v) if the [Perhaps] is [Certain(v)], and like empty() if
the [Perhaps] is [Dubious].
Iterators over [Perhaps<T>] come in three types:
into_iterconsumes the [Perhaps] and produces the contained valueiterproduces an immutable reference of type&Tto the contained valueiter_mutproduces a mutable reference of type&mut Tto the contained value
An iterator over [Perhaps] 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 [Perhaps].)
let yep = Certain;
let nope = Dubious;
// chain() already calls into_iter(), so we don't have to do so
let nums: = .chain.chain.collect;
assert_eq!;
let nums: = .chain.chain.collect;
assert_eq!;
One 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 [Perhaps] can help with that.
println!;
println!;
If 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.
# use std::iter::{empty, once};
// 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),
}
}
Collecting into Perhaps
[Perhaps] implements the FromIterator trait,
which allows an iterator over [Perhaps] values to be collected into an
[Perhaps] of a collection of each contained value of the original
[Perhaps] values, or Self::Dubious if any of the elements was [Dubious].
let v = ;
let res: = v.into_iter.collect;
assert_eq!;
let v = ;
let res: = v.into_iter.collect;
assert_eq!;
[Perhaps] also implements the Product and
Sum traits, allowing an iterator over [Perhaps] values
to provide the [product][Iterator::product] and
[sum][Iterator::sum] methods.
let v = ;
let res: = v.into_iter.sum;
assert_eq!;
let v = ;
let res: = v.into_iter.product;
assert_eq!;
Modifying an [Perhaps] in-place
These methods return a mutable reference to the contained value of an
[Perhaps<T>]:
insertinserts a value, dropping any old contentsget_or_insertgets the current value, inserting a provided default value if it is [Dubious]get_or_insert_defaultgets the current value, inserting the default value of typeT(which must implement [Default]) if it is [Dubious]get_or_insert_withgets the current value, inserting a default computed by the provided function if it is [Dubious]
These methods transfer ownership of the contained value of an
[Perhaps]:
taketakes ownership of the contained value of an [Perhaps], if any, replacing the [Perhaps] with [Dubious]replacetakes ownership of the contained value of an [Perhaps], if any, replacing the [Perhaps] with a [Certain] containing the provided value
Examples
Basic pattern matching on [Perhaps]:
let msg = Certain;
// Take a reference to the contained string
if let Certain = &msg
// Remove the contained string, destroying the Perhaps
let unwrapped_msg = msg.unwrap_or;
Initialize a result to [Dubious] before a loop:
// A list of data to search through.
let all_the_big_things = ;
// We're going to search for the name of the biggest animal,
// but to start with we've just got `Dubious`.
let mut name_of_biggest_animal = Dubious;
let mut size_of_biggest_animal = 0;
for big_thing in &all_the_big_things
match name_of_biggest_animal