Dynamic Futures

#1

So - trying to get something that changes at runtime but satisfies a particular Trait… seems it’s either:

  1. Wrap everything in enums
  2. Use Trait Objects

Fine. But now I want to get that as a Future… going the Trait Objects route, seems that I then need to also have the Future be a Trait Object, which means it needs to be boxed as well? (not totally clear on why… just where I ended up with fighting the compiler)

Here’s the best I could do. Any tips?

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=70255f15582a65140fd73f6be2c3f3ff

use std::fmt::{Display, Formatter, Result};
use futures::*;

struct Foo {}
struct Bar {}

impl Display for Foo {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "foo")
    }
}

impl Display for Bar {
    fn fmt(&self, f: &mut Formatter) -> Result {
        write!(f, "bar")
    }
}

impl Foo {
    fn new() -> impl Future<Item = Box<Foo>, Error = ()> {
        future::ok(Box::new(Foo{}))
    }
}

impl Bar {
    fn new() -> impl Future<Item = Box<Bar>, Error = ()> {
        future::ok(Box::new(Bar{}))
    }
}

//Can't do this
/*
fn get_dynamic_future (name:&str) -> impl Future<Item = Box<dyn Display + Send>, Error = ()> {
    match name {
        "foo" => Foo::new().map(|v| v as Box<Display + Send>),
        "bar" => Foo::new().map(|v| v as Box<Display + Send>),
        _ => future::err(())
    }
}
*/

//Gotta do this??
fn get_dynamic_future (name:&str) -> Box<dyn Future<Item = Box<dyn Display + Send>, Error = ()> + Send> {
    match name {
        "foo" => Box::new(Foo::new().map(|v| v as Box<Display + Send>)),
        "bar" => Box::new(Bar::new().map(|v| v as Box<Display + Send>)),
        _ => Box::new(future::err(()))
    }
}

fn main() {
    
    tokio::run(get_dynamic_future("foo").map(|value| {
        println!("{}", value);
        ()
    }));

    tokio::run(get_dynamic_future("bar").map(|value| {
        println!("{}", value);
        ()
    }));

}
#2

Yes, impl Future is still one exact type, you just don’t need to type it. It’s closer to type inference than an abstraction.

Box<dyn Future> is the way to go. Either may be OK too. Each future type can have a different size, so you can’t return it without boxing. The caller wouldn’t know how much space to reserve and how to read that type without some kind of tag… like enum.

3 Likes