Returning multiple futures

It seems if there are different types of futures they need to be boxed (or some other kind of unifying container).

Trying Box<> itself didn't work for me... had to do the following. Is this right (it does work, just not 100% sure)?

(can't put on playground since it doesn't support futures 0.3 yet)

#![feature(async_await)]
use futures::{Future, executor};
use futures::future::{self, join_all, FutureExt};
use core::pin::Pin;

type StringResult = Result<String, String>;

// Attempt 1: impl Future
// fails because the match arms are different types
/*
fn get_future(key:&str) -> impl Future<Output = StringResult> {
    let key_owned = key.to_owned();

    match key {
        "hello" => future::ok(key_owned),
        "world" => future::lazy(|_| Ok(key_owned)),
        _ => future::err(format!("bad key: {}", key)),
    }
}
*/

//Attempt 2: Box<dyn Future>
//fails because... Pin requirements?
/*
fn get_future(key:&str) -> Box<dyn Future<Output = StringResult>> {
    let key_owned = key.to_owned();

    match key {
        "hello" => Box::new(future::ok(key_owned)),
        "world" => Box::new(future::lazy(|_| Ok(key_owned))),
        _ => Box::new(future::err(format!("bad key: {}", key))),
    }
}
*/

//Attempt 3: Pin<Box<Future>> (via future.boxed())
//It works! Requires the FutureExt trait be in scope
fn get_future(key:&str) -> Pin<Box<dyn Future<Output = StringResult>>> {
    let key_owned = key.to_owned();

    match key {
        "hello" => future::ok(key_owned).boxed(),
        "world" => future::lazy(|_| Ok(key_owned)).boxed(),
        _ => future::err(format!("bad key: {}", key)).boxed(),
    }
}

fn main() {
    executor::block_on(async {
        let results = join_all(vec![get_future("hello"), get_future("world"), get_future("!")]).await;

        for result in results.iter() {
            match result {
                Ok(happy) => println!("happy: {}", happy),
                Err(sad) => println!("sad: {}", sad)
            }
        }
    });
}

But now when I try and apply that in real project, I'm getting

*mut u8 cannot be sent between threads safely

Can't seem to duplicate it in a standalone example though, even changing the above to Vec<u8> :\

I guess that's because of where it's being passed, i.e. for wasm... dunno

EDIT: tried all kinds of things, playing with FutureExt and FutureObj... kinda stuck... and gotta run soon - will come back to this in a couple days but suggestions are greatly appreciated in the meantime, even if I can't try them out yet

There is an Either type to support this sort of usecase. The eventual solution is async/await syntax, by .awaiting different futures inside the constructed future the outside will not have to see which is being used, e.g.

fn get_future(key:&str) -> impl Future<Output = StringResult> {
    let key_owned = key.to_owned();

    async {
        match key {
            "hello" => key_owned,
            "world" => {
                whatever_lazy_was_representing().await;
                Ok(key_owned)
            }
            _ => Err(format!("bad key: {}", key)),
        }
    }
}
1 Like

Hmm Either looks interesting, thanks!

Re: async/await, the problem is I don't want to await on each future individually - I want to collect them all into a Vec and try_join_all() so that they run in parallel

By wrapping the insides of get_future in an async block you will cause it to return just one type of future, it can internally await different futures without revealing that fact to the caller of get_future, and you can still collect the wrapped futures and run them in parallel.

1 Like
  • I don't know if it will support the exact Future trait you are working with, but if it doesn't, you can raise an issue for it to be supported
1 Like