В чем разница между `then`, `and_then` и `or_else` в Rust futures?

#rust #future

#Ржавчина #будущее

Вопрос:

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

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

Ответ №1:

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

and_then и or_else являются прямыми аналогами методов с тем же именем на Result .


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

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

FutureExt::then :

Эта функция может быть использована для обеспечения того, чтобы вычисление выполнялось независимо от будущего завершения. Предоставленное закрытие будет произведено Result после завершения будущего.

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

FutureExt::and_then :

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

FutureExt::or_else

Возвращает future, который передает значение этого future, если оно выполнено успешно, а в противном случае передает ошибку в закрытие f и ожидает будущего, которое оно возвращает.

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

  • then : никаких дополнительных ограничений на возвращаемый тип.
  • and_then требуется, чтобы тип ошибки возвращаемого future соответствовал типу ошибки начального future.
  • 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(())
}
  

Комментарии:

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

2. Поскольку and_then , then и or_else не фигурируют в последнем фрагменте, означает ли это, что никто никогда не использует их в асинхронном коде Rust > 1.39? (Если только не писать код асинхронной библиотеки, который в любом случае не может извлечь выгоду из async / await синтаксиса?)

3. @jsstuball Я не скажу, что никогда , но да, сейчас они в основном устарели. Вы также можете использовать async / await синтаксис в библиотечном коде.