6

I am used to Scala's Future type where you wrap whatever object you're returning in Future[..] to designate it as such.

My Rust function hello returns Query and I don't seem able to pass that result as an argument with type Future<Output = Query>. Why not and how should I type this better?

The failure occurs when I try to pass the future as an argument:

use std::future::Future;

struct Person;
struct DatabaseError;

type Query = Result<Vec<Person>, DatabaseError>;

async fn hello_future(future: &dyn Future<Output = Query>) -> bool {
    future.await.is_ok()
}

async fn hello() -> Query {
    unimplemented!()
}

async fn example() {
    let f = hello();
    hello_future(&f);
}

fn main() {}

Which fails to compile with the error:

error[E0277]: `&dyn Future<Output = Result<Vec<Person>, DatabaseError>>` is not a future
 --> src/main.rs:9:5
  |
9 |     future.await.is_ok()
  |     ^^^^^^^^^^^^ `&dyn Future<Output = Result<Vec<Person>, DatabaseError>>` is not a future
  |
  = help: the trait `Future` is not implemented for `&dyn Future<Output = Result<Vec<Person>, DatabaseError>>`
  = note: required by `poll`
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
ecoe
  • 4,994
  • 7
  • 54
  • 72
  • keep in mind I'm connecting over local host, so even if I paste each and every character, there's still the entire database missing. I'd just return dummy data, but then the interaction with `tokio_postgres` would be lost, hence my post the way it is... Is that reasonable? – ecoe Aug 31 '21 at 18:57
  • *there's still the entire database missing* — that's true, but that is a runtime requirement. You have a compilation error, which is far before even attempting to run the program, much less connect to the database. – Shepmaster Aug 31 '21 at 19:05
  • ah, yeah makes sense since compile error running end2end not needed – ecoe Aug 31 '21 at 19:15
  • 1
    Another route: why did you want to pass the _future_ to the function, instead of just passing in `Query`? The first thing you do is `.await` it. Even if you wanted to pass the future, does `(future: impl Future)` work in your real case? – Shepmaster Aug 31 '21 at 19:20
  • now I'm even more confused, yes that `impl` does work! – ecoe Aug 31 '21 at 19:33

2 Answers2

7

async functions desugar to returning an opaque value that implements the Future trait. This means that you can accept a generic type that implements that trait. The most succinct syntax is impl Trait, but you could also introduce a named generic parameter:

async fn hello_future(future: impl Future<Output = Query>) -> bool {
    future.await.is_ok()
}

async fn example() {
    let f = hello();
    hello_future(f);
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
1

You can also use Pin<Box<dyn Future<Output = Query>>> as parameter:

async fn hello_future(future: Pin<Box<dyn Future<Output = Query>>>) -> bool {
    future.await.is_ok()
}
...
async fn example() {
    let f: impl Future<Output = Result<Vec<Person>, DatabaseError>> = hello();
    hello_future(Box::pin(f));
}

The difference is that impl Future is compile-time dispatch. Compiler generates executable code for each per-type instantiation of hello_future. And Pin<Box<...>> is runtime dispatch. Only one copy of the executable binary code for hello_future is generated.

fred xia
  • 151
  • 3