В чем разница между `then`, `and_then` и `or_else` в фьючерсах Rust?

Я учусь использовать фьючерсы Rust, и я нахожу это чрезвычайно запутанным. Я чувствую, что веду себя глупо, но когда можно использовать then, and_then и or_else? Какие типы возврата ожидаются?

Пожалуйста, приведите несколько примеров различных ситуаций, которые вы ожидаете увидеть.

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
11
0
9 665
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

TL;DR: then используется, когда вы хотите что-то сделать независимо от того, было ли будущее успешным или нет, and_then запускает закрытие только тогда, когда будущее успешно, а or_else запускает закрытие, только когда будущее не удалось.

and_then и or_else — прямые аналоги одноименных методов на Result.


Ваш первый шаг должен состоять в том, чтобы прочитать документы. Документация содержит точные сигнатуры методов (которые объясняют, какие типы он ожидает и какие типы возвращаемых данных), подробное описание каждого метода, а также примеры использования.

Я извлек небольшие фрагменты документов и подчеркнул соответствующие части.

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.

Тип возвращаемого значения для всех трех методов — любой тип, который можно преобразовать в другое будущее.

  • then: никаких дополнительных ограничений на возвращаемый тип.
  • and_then требует, чтобы тип ошибки возвращаемого будущего соответствовал типу ошибки начального будущего.
  • or_else требует, чтобы тип успеха возвращаемого будущего соответствовал типу успеха начального будущего.

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(())
    })
}

Некоторые случаи упрощаются синтаксисом async/await, стабилизированным в 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(())
}

Спасибо, что указали на документы и научили их использовать, чтобы иметь возможность самостоятельно находить ответы!

LittleTiger 06.11.2019 02:21

Другие вопросы по теме