Как я могу условно вернуть различные типы фьючерсов?


У меня есть метод, который, в зависимости от предиката, вернет то или иное будущее. Другими словами, выражение if-else, которое возвращает будущее:

extern crate futures; // 0.1.23

use futures::{future, Future};

fn f() -> impl Future<Item = usize, Error = ()> {
    if 1 > 0 {
        future::ok(2).map(|x| x)
    } else {
        future::ok(10).and_then(|x| future::ok(x + 2))
    }
}

Это не компилируется:

error[E0308]: if and else have incompatible types
  --> src/lib.rs:6:5
   |
6  | /     if 1 > 0 {
7  | |         future::ok(2).map(|x| x)
8  | |     } else {
9  | |         future::ok(10).and_then(|x| future::ok(x + 2))
10 | |     }
   | |_____^ expected struct `futures::Map`, found struct `futures::AndThen`
   |
   = note: expected type `futures::Map<futures::FutureResult<{integer}, _>, [closure@src/lib.rs:7:27: 7:32]>`
              found type `futures::AndThen<futures::FutureResult<{integer}, _>, futures::FutureResult<{integer}, _>, [closure@src/lib.rs:9:33: 9:54]>`
Фьючерсы создаются по-разному и могут содержать замыкания, поэтому их типы не равны. В идеале, решение не будет использовать Boxes, так как остальная часть моей асинхронной логики не использует их.

, Какой-то логики в фьючерсы обычно делается?

1 4

1 ответ:

Either

Использование futures::future::Either не имеет дополнительного выделения кучи:

extern crate futures; // 0.1.23

use futures::{
    future::{self, Either},
    Future,
};

fn f() -> impl Future<Item = usize, Error = ()> {
    if 1 > 0 {
        Either::A(future::ok(2).map(|x| x))
    } else {
        Either::B(future::ok(10).and_then(|x| future::ok(x + 2)))
    }
}

Однако для этого требуется фиксированное распределение стека. Если A занимает 1 байт и происходит 99% времени, но B занимает 512 байт, ваш Either будет Всегда занимать 512 байт (плюс некоторые). Это не всегда победа.

Коробочные объекты признаков

extern crate futures; // 0.1.23

use futures::{future, Future};

fn f() -> Box<Future<Item = usize, Error = ()>> {
    if 1 > 0 {
        Box::new(future::ok(2).map(|x| x))
    } else {
        Box::new(future::ok(10).and_then(|x| future::ok(x + 2)))
    }
}

Как указывает Матье м. , два решения могут быть объединены:

Я бы отметил что существует среднее решение для случая Большого B: Either(A, Box<B>). Таким образом, вы платите только за выделение кучи в том редком случае, когда это B

Обратите внимание, что вы также можете стек Eithers, Если у вас есть более 2 Условий (Either<A, Either<B, C>>; Either<Either<A, B>, Either<C, D>>, и т.д.):

См. также: