Rust Async / ждут в штучной упаковке будущего - PullRequest
1 голос
/ 06 марта 2020

Как можно ждать результата в штучной упаковке?

use futures::{future, Future};

fn test() -> Box<dyn Future<Output = bool>> {
    Box::new(future::ok::<bool>(true))
}

async fn async_fn() -> bool {
    let result: bool = test().await;
    return result;
}

fn main(){
    async_fn();
    println!("Hello!");
}

Пожалуйста, смотрите на Детская площадка

Ошибка:

error[E0277]: the trait bound `dyn core::future::future::Future<Output = bool>: std::marker::Unpin` is not satisfied
  --> src/main.rs:8:24
   |
8  |     let result: bool = test().await;
   |                        ^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `dyn core::future::future::Future<Output = bool>`
   |
   = note: required because of the requirements on the impl of `core::future::future::Future` for `std::boxed::Box<dyn core::future::future::Future<Output = bool>>`

Ответы [ 2 ]

4 голосов
/ 06 марта 2020

Когда дело доходит до Box и future, почти всегда имеет смысл использовать Box::pin вместо Box::new:

use std::pin::Pin;
use futures::{future, Future};

fn test() -> Pin<Box<dyn Future<Output = Result<bool, ()>>>> {
    Box::pin(future::ok(true))
}

async fn async_fn() -> bool {
    test().await.unwrap()
}

Причина довольно интересная. Pin имеет общую реализацию для Unpin:

impl<P> Unpin for Pin<P> where
    P: Unpin,

И Box<T> внутри него безусловно Unpin:

impl<T> Unpin for Box<T> where
    T: ?Sized,

То есть Pin<Box<dyn Future>> - это не закрепленное Future. Все работает, но почему Box само по себе нет? Это одно место, где Deref мешает:

impl<T: ?Sized> Deref for Box<T> {
    type Target = T;
}

await ожидает открепления Future, а Box<dyn Future>, созданный с помощью Box::new, содержит Future. Таким образом, он автоматически разыменовывается, и Unpin теряется, если вы явно не заявите об этом с помощью Box<dyn Future + Unpin>.


Редактировать: @ ÖmerErden прав в том, почему Box<dyn Future> не будет работать.

3 голосов
/ 06 марта 2020

В соответствии с реализацией ( ссылка ):

impl<F> Future for Box<F>
where
    F: Unpin + Future + ?Sized, 

Фьючерсы в штучной упаковке реализуют черту Future только тогда, когда будущее внутри Box реализует Unpin.

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

Итак, одно решение от @Stargateur, добавление явной границы типа к сигнатуре работает ( Playground ):

fn test() -> Box<dyn Future<Output = Result<bool, ()>> + Unpin> 

Но если вы используете futures-rs, то есть вспомогательный тип с именем futures::future::BoxFuture, вы можете использовать BoxedFuture без явного указания Unpin, например:

use futures::future::BoxFuture;

fn test() -> BoxFuture<'static, Result<bool, ()>> {
    Box::pin(async { Ok(true) })
}

Playground

...