Optional values.
Type [Perhaps] represents an optional value: every [Perhaps]
is either Self::Certain and contains a value, or Self::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
Self::Dubiousis 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 Self::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(...)) or
not (Self::Dubious).
let optional = SelfDubious;
check_optional;
let optional = SelfCertain;
check_optional;
The question mark operator, ?
Similar to the [Result] type, when writing code that calls many functions that return the
[Perhaps] type, handling Self::Certain/Self::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 Self::Certain's unwrapped value, unless the
result is Self::Dubious, in which case Self::Dubious is returned early from the enclosing function.
? can be used in functions that return [Perhaps] because of the
early return of Self::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>::Self::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 Self::Certain::<T>(_) to T (but transmuting Self::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_some and is_none methods return [true] if the [Perhaps]
is Self::Certain or Self::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 Self::Certain variant. If the [Perhaps] is Self::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_ortransformsSelf::Certain(v)toOk(v), andSelf::DubioustoErr(err)using the provided defaulterrvalueok_or_elsetransformsSelf::Certain(v)toOk(v), andSelf::Dubiousto a value of [Err] using the provided functiontransposetransposes an [Perhaps] of a [Result] into a [Result] of an [Perhaps]
These methods transform the Self::Certain variant:
filtercalls the provided predicate function on the contained valuetif the [Perhaps] isSelf::Certain(t), and returnsSelf::Certain(t)if the function returnstrue; otherwise, returnsSelf::Dubiousflattenremoves one level of nesting from an [Perhaps<Perhaps<T>>]maptransforms [Perhaps<T>] to [Perhaps<U>] by applying the provided function to the contained value ofSelf::Certainand leavingSelf::Dubiousvalues unchanged
These methods transform [Perhaps<T>] to a value of a possibly
different type U:
map_orapplies the provided function to the contained value ofSelf::Certain, or returns the provided default value if the [Perhaps] isSelf::Dubiousmap_or_elseapplies the provided function to the contained value ofSelf::Certain, or returns the result of evaluating the provided fallback function if the [Perhaps] isSelf::Dubious
These methods combine the Self::Certain variants of two [Perhaps] values:
zipreturnsSelf::Certain((s, o))ifselfisSelf::Certain(s)and the provided [Perhaps] value isSelf::Certain(o); otherwise, returnsSelf::Dubiouszip_withcalls the provided functionfand returnsSelf::Certain(f(s, o))ifselfisSelf::Certain(s)and the provided [Perhaps] value isSelf::Certain(o); otherwise, returnsSelf::Dubious
Boolean operators
These methods treat the [Perhaps] as a boolean value, where Self::Certain
acts like [true] and Self::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) | Self::Dubious |
and |
Self::Certain(x) |
Self::Dubious |
Self::Dubious |
and |
Self::Certain(x) |
Self::Certain(y) |
Self::Certain(y) |
or |
Self::Dubious |
Self::Dubious |
Self::Dubious |
or |
Self::Dubious |
Self::Certain(y) |
Self::Certain(y) |
or |
Self::Certain(x) |
(ignored) | Self::Certain(x) |
xor |
Self::Dubious |
Self::Dubious |
Self::Dubious |
xor |
Self::Dubious |
Self::Certain(y) |
Self::Certain(y) |
xor |
Self::Certain(x) |
Self::Dubious |
Self::Certain(x) |
xor |
Self::Certain(x) |
Self::Certain(y) |
Self::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) | Self::Dubious |
and_then |
Self::Certain(x) |
x |
Self::Dubious |
Self::Dubious |
and_then |
Self::Certain(x) |
x |
Self::Certain(y) |
Self::Certain(y) |
or_else |
Self::Dubious |
(not provided) | Self::Dubious |
Self::Dubious |
or_else |
Self::Dubious |
(not provided) | Self::Certain(y) |
Self::Certain(y) |
or_else |
Self::Certain(x) |
(not provided) | (not evaluated) | Self::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 (Self::Dubious) through unchanged, and continue processing on
success values (Self::Certain). Toward the end, or substitutes an error
message if it receives Self::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, Self::Dubious compares as
less than any Self::Certain, and two Self::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 Self::Certain), or produce no values
(when the [Perhaps] is Self::Dubious). For example, into_iter acts like
once(v) if the [Perhaps] is Self::Certain(v), and like empty() if
the [Perhaps] is Self::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 = SelfCertain;
let nope = SelfDubious;
// 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 Self::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 isSelf::Dubiousget_or_insert_defaultgets the current value, inserting the default value of typeT(which must implement [Default]) if it isSelf::Dubiousget_or_insert_withgets the current value, inserting a default computed by the provided function if it isSelf::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] withSelf::Dubiousreplacetakes ownership of the contained value of an [Perhaps], if any, replacing the [Perhaps] with aSelf::Certaincontaining the provided value
Examples
Basic pattern matching on [Perhaps]:
let msg = SelfCertain;
// Take a reference to the contained string
if let SelfCertain = &msg
// Remove the contained string, destroying the Perhaps
let unwrapped_msg = msg.unwrap_or;
Initialize a result to Self::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 `Self::Dubious`.
let mut name_of_biggest_animal = SelfDubious;
let mut size_of_biggest_animal = 0;
for big_thing in &all_the_big_things
match name_of_biggest_animal