Essential combinators
We saw a few of the most important combinators in thefutures andstreams overviews. Here we’ll take alook at a few more. It’s also worth spending some time with the traitdocumentation to familiarize yourself with the full range of combinatorsavailable (cheatsheet).
Some concrete futures and streams
Any value can be turned into an immediately complete future. There are a fewfunctions in the future
module for creating such a future:
ok
, which is analogous toResult::Ok
: it treats the value you give it as an immediately successful future.err
, which is analogous toResult::Err
: it treats the value you give it as an immediately failed future.result
, which lifts a result to an immediately-complete future.
For streams, there are a few equivalents of an “immediately ready” stream:iter
, which creates a stream that yields the same items as the underlyingiterator. The iterator producesResult
values, and the first error terminatesthe stream with that error.once
, which creates a single-element stream from aResult
.
In addition to these constructors, there’s also a function,lazy
, whichallows you to construct a future given a closure that will produce that futurelater, on demand.
IntoFuture
A crucial API to know about is the IntoFuture
trait, which is a trait forvalues that can be converted into futures. Most APIs that you think of as takingfutures actually work with this trait instead. The key reason: the trait isimplemented for Result
, allowing you to return Result
values in many placesthat futures are expected.
Adapters
Like Iterator
, the Future
, Stream
and Sink
traits all come equippedwith a broad range of “adapter” methods. These methods all consume the receivingobject and return a new, wrapped one. For futures, you can use adapters to:
- Change the type of a future (
map
,map_err
) - Run another future after one has completed (
then
,and_then
,or_else
) - Figure out which of two futures resolves first (
select
) - Wait for two futures to both complete (
join
) - Convert to a trait object (
Box::new
) Convert unwinding into errors (
catch_unwind
)
For streams, there are a large set of adapters, including:Many in common with
Iterator
, likemap
,fold
,collect
,filter
,zip
,take
,skip
and so on. Note thatfold
andcollect
produce futures, and hence their result is computedasynchronously.- Adapters for sequencing with futures (
then
,and_then
,or_else
) - Additional adapters for combining streams (
merge
,select
)
TheSink
trait currently has fewer adapters
Finally, an object that is both a stream and a sink can be broken into separatestream and sink objects using the split
adapter.
All adapters are zero-cost, meaning that no memory is allocated internally andthe implementation will optimize to what you would have otherwise written byhand.
Error handling
Futures, streams and sinks all treat error handling as a core concern: they areall equipped with an associated error type, and the various adapter methodsinterpret errors in sensible ways. For example:
The sequencing combinators
then
,and_then
,or_else
,map
, andmap_err
all chain errors similarly to theResult
type in the standardlibrary. So, for example, if you chain futures usingand_then
and thefirst future fails with an error, the chained future is never run.Combinators like
select
andjoin
also deal with errors. Forselect
, the first future to complete in any way yields an answer,propagating the error, but also giving access to the other future should youwant to keep working with it. Forjoin
, if any future produces an error,the entire join produces that error.
By default, futures don’t have any special handling for panics. In most cases,though, futures are ultimately run as tasks within a thread pool, where you’llwant to catch any panic they produce and propagate that elsewhere. Thecatch_unwind
adapter can be used to reify a panic into a Result
withouttaking down the worker thread.
Next up: Returning futures