Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I store an `impl Future` as a concrete type?

tokio::net::TcpStream::connect is an async function, meaning it returns an existential type, impl Future. I would like to store a Vec of these futures in a struct. I've found many questions where someone wants to store multiple different impl Futures in a list, but I only want to store the return type of one. I feel like this should be possible without Box<dyn Future> as I am really only storing a single concrete type, but I cannot figure out how without getting found opaque type errors.

like image 215
Ibraheem Ahmed Avatar asked Oct 28 '25 01:10

Ibraheem Ahmed


1 Answers

It is possible with the nightly feature min_type_alias_impl_trait. The trick is to create a type alias, and a dummy function from which the compiler can infer a defining use.

#![feature(min_type_alias_impl_trait)]

use tokio::net::TcpStream;
use core::future::Future;

type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;

fn __tcp_stream_connect_defining_use() -> TcpStreamConnectFut  {
    TcpStream::connect("127.0.0.1:8080")
}

struct Foo {
    connection_futs: Vec<TcpStreamConnectFut>,
}

This compiles, but does not work as expected:

impl Foo {
    fn push(&mut self) {
        self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
    }
}
error[E0308]: mismatched types
   --> src/lib.rs:18:35
    |
6   | type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
    |                            ------------------------------------------------ the expected opaque type
...
18  |         self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
    |
    = note: while checking the return type of the `async fn`
    = note: expected opaque type `impl Future` (opaque type at <src/lib.rs:6:28>)
               found opaque type `impl Future` (opaque type at </playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.7.1/src/net/tcp/stream.rs:111:56>)
    = help: consider `await`ing on both `Future`s
    = note: distinct uses of `impl Trait` result in different opaque types 

Using the dummy function we created does work:

impl Foo {
    fn push(&mut self) {
        self.connection_futs.push(__tcp_stream_connect_defining_use());
    }
}

So we can just create wrapper functions:

fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut  {
    TcpStream::connect(addr)
}

Except...

error: type parameter `A` is part of concrete type but not used in parameter list for the `impl Trait` type alias
  --> src/main.rs:9:74
   |
9  |   fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut  {
   |  __________________________________________________________________________^
10 | |     TcpStream::connect(addr)
11 | | }
   | |_^

We could just use String or &'static str, and the entire thing compiles:

type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;

fn tcp_stream_connect(addr: &'static str) -> TcpStreamConnectFut  {
    TcpStream::connect(addr)
}

struct Foo {
    connection_futs: Vec<TcpStreamConnectFut>,
}

impl Foo {
    fn push(&mut self) {
        self.connection_futs.push(tcp_stream_connect("..."));
    }
}

You can also add a generic parameter to the type alias itself, but that probably doesn't make sense in this case:

type TcpStreamConnectFut<A> = impl Future<Output = std::io::Result<TcpStream>>;

fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut<A>  {
    TcpStream::connect(addr)
}

struct Foo {
    connection_futs: Vec<TcpStreamConnectFut<&'static str>>,
}

impl Foo {
    fn push(&mut self) {
        self.connection_futs.push(tcp_stream_connect("..."));
    }
}

So it is possible, but there are a couple of restrictions. I'm not sure how many of these are bugs, and how much of it is intentional behavior. There has been discussion about a typeof operator to make this easier, but this is what we've got at the moment.

like image 80
Ibraheem Ahmed Avatar answered Oct 29 '25 14:10

Ibraheem Ahmed