3

I'm trying to asynchronously invoke a Future that is returned from a function. Is it possible?

use core::future::Future;

fn choose_your_adventure<'a>(i: usize) -> Box<&'a dyn Future<Output = ()>> {
    match i {
        0 => Box::new(&async {}),
        _ => Box::new(&async {})
    }
}

async fn does_not_work() -> () {
    let choice = choose_your_adventure(0);
    choice.await; // error[E0277]: `&dyn Future<Output = ()>` is not a future
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Matt Thomas
  • 5,279
  • 4
  • 27
  • 59
  • 1
    If `&T` is a `Future`, just return `&T` as a `dyn Future`. Although normally you would be working with owned future types. – Ibraheem Ahmed May 03 '21 at 18:21

1 Answers1

5

No. Polling a future requires that it be mutable. An immutable reference cannot be mutated, by design.

In this case you don't need a Box<&dyn ...>. I'd write your code without the trait object:

async fn choose_your_adventure(i: usize) {
    match i {
        0 => (),
        _ => (),
    }
}

If you have your heart set on a trait object, then there's no need to have a boxed reference:

fn choose_your_adventure(i: usize) -> Box<dyn Future<Output = ()>> {
    match i {
        0 => Box::new(async {}),
        _ => Box::new(async {}),
    }
}

Although it may be better to use type aliases from the futures crate:

use futures::{future::BoxFuture, FutureExt}; // 0.3.14

fn choose_your_adventure(i: usize) -> BoxFuture<'static, ()> {
    match i {
        0 => async {}.boxed(),
        _ => async {}.boxed(),
    }
}

Notably, that resolves to Pin<Box<dyn Future<Output = T> + 'a + Send>>, which indicates that the future may be unpinned (if T: Unpin).

See also:

Timmmm
  • 88,195
  • 71
  • 364
  • 509
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • "Polling a future requires that it be mutable" Doh! I should have known that. "Instead, return the boxed trait object" When I try `dyn Future` then it tells me it doesn't implement Unpin https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7e747fdd0d75e0e77a5e49e78c03e3f5 adding +Unpin just raises another issue about how something "cannot be unpinned". Is it possible to asynchronously invoke the returned thing? – Matt Thomas May 03 '21 at 18:27
  • 1
    @MattThomas `async` blocks cannot be unpinned. You can return a `Pin>>` instead (or use the `BoxFuture` type alias) and `Box::pin` the future. – Ibraheem Ahmed May 03 '21 at 20:58