web-dev-qa-db-ja.com

さまざまな種類の先物を条件付きで返すにはどうすればよいですか?

述語に応じて、ある未来または別の未来を返すメソッドがあります。言い換えると、futureを返す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を使用しないでしょう。これは、私の非同期ロジックの残りの部分がそれらを使用しないためです。

先物のif-elseロジックは通常どのように実行されますか?

13

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

Matthieu M.が指摘 のように、2つのソリューションを組み合わせることができます。

大きなBEither(A, Box<B>)の場合の中間的な解決策があることに注意してください。このように、ヒープ割り当ての料金は、それがBであるまれなケースでのみ支払われます。

3つ以上の条件(_Either<A, Either<B, C>>_; _Either<Either<A, B>, Either<C, D>>_など)がある場合は、Eithersをスタックすることもできることに注意してください。

_fn f(v: i32) -> impl Future<Item = i32, Error = ()> {
    use std::cmp::Ordering;
    match v.cmp(&0) {
        Ordering::Less => Either::A(future::ok(2).map(|x| -x)),
        Ordering::Equal => Either::B(Either::A(future::ok(0))),
        Ordering::Greater => Either::B(Either::B(future::ok(-2).map(|x| x * x))),
    }
}
_

参照:

19
Shepmaster