TL;DR: then
is used when you want to do something regardless of if the future was successful or not, and_then
runs the closure only when the future succeeded, and or_else
runs the closure only when the future failed.
and_then
and or_else
are direct analogs to the methods of the same name on Result
.
Your first step should be to read the docs. The documentation contains the exact method signatures (which explain what types it expects and what the return types are), prose describing each method, and example usage as well.
I've extracted small snippets of the docs and emphasized the relevant parts.
FutureExt::then
:
This function can be used to ensure a computation runs regardless of
the conclusion of the future. The closure provided will be yielded a
Result
once the future is complete.
The returned value of the closure must implement the IntoFuture
trait
and can represent some more work to be done before the composed future
is finished.
FutureExt::and_then
:
This function can be used to chain two futures together and ensure
that the final future isn't resolved until both have finished. The
closure provided is yielded the successful result of this future and
returns another value which can be converted into a future.
FutureExt::or_else
Return a future that passes along this future's value if it succeeds, and otherwise passes the error to the closure f
and waits for the future it returns.
The return type for all three methods is any type that can be converted into another future.
then
: no additional restrictions on the return type.
and_then
requires that the error type of the returned future match the starting future's error type.
or_else
requires that the success type of the returned future match the starting future's success type.
use futures::{future, Future}; // 0.1.25
struct Error;
fn download_from_server(server: u8) -> impl Future<Item = Vec<u8>, Error = Error> {
/* ... */
}
fn upload_to_server(data: Vec<u8>) -> impl Future<Item = usize, Error = Error> {
/* ... */
}
// Uses `or_else` to do work on failure
fn download() -> impl Future<Item = Vec<u8>, Error = Error> {
download_from_server(0)
.or_else(|_| download_from_server(1))
.or_else(|_| download_from_server(2))
}
// Uses `and_then` to do work on success
fn reupload() -> impl Future<Item = usize, Error = Error> {
download().and_then(|data| upload_to_server(data))
}
// Uses `then` to always do work
fn do_things() -> impl Future<Item = (), Error = ()> {
reupload().then(|r| {
match r {
Ok(size) => println!("Uploaded {} bytes", size),
Err(_) => println!("Got an error"),
};
Ok(())
})
}
Some cases are simplified by the async
/ await
syntax stabilized in Rust 1.39:
// Equivalent to `or_else`
async fn download() -> Result<Vec<u8>, Error> {
match download_from_server(0).await {
Ok(v) => Ok(v),
Err(_) => match download_from_server(1).await {
Ok(v) => Ok(v),
Err(_) => download_from_server(2).await,
},
}
}
// Equivalent to `and_then`
async fn reupload() -> Result<usize, Error> {
let data = download().await?;
upload_to_server(data).await
}
// Equivalent to `then`
async fn do_things() -> Result<(), ()> {
match reupload().await {
Ok(size) => println!("Uploaded {} bytes", size),
Err(_) => println!("Got an error"),
}
Ok(())
}