Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between `then`, `and_then` and `or_else` in Rust futures?

Tags:

rust

future

I am learning to use Rust futures and I am finding it extremely confusing. I feel like I am being stupid but when would then, and_then and or_else be used? What return types are expected?

Please provide some examples of the different situations you would expect to see them.

like image 617
Enayet Hussain Avatar asked Apr 06 '19 18:04

Enayet Hussain


1 Answers

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.

Future::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.

Future::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.

Future::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(())
}
like image 188
Shepmaster Avatar answered Jan 04 '23 12:01

Shepmaster