Expected opaque type, found a different opaque type when trying futures::join_all

Hello,

I'm trying to run multiple futures in parallel with join_all but I get a different opaque type error and I haven't been able to find why. Can somebody point me in the right direction?

The error I get is

error[E0308]: mismatched types
   --> src/main.rs:86:9
    |
86  |         get_blobs_from_table(pool, &blobs, "TestTable"),
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
...
131 | ) -> Result<ShardBlobCounts, sqlx::Error> {
    |      ------------------------------------ the `Output` of this `async fn`'s found opaque type
    |
    = note:     expected type `impl core::future::future::Future` (opaque type at <src/main.rs:109:6>)
            found opaque type `impl core::future::future::Future` (opaque type at <src/main.rs:131:6>)
    = note: distinct uses of `impl Trait` result in different opaque types

The code in question is doing

let futures = vec![
        get_blobs_from_data(pool, &blobs),
        get_blobs_from_table(pool, &blobs, "TestTable"),
        get_blobs_from_table(pool, &blobs, "OtherTestTable"),
    ];

and the signatures for those functions are:

async fn get_blobs_from_data(
    pool: &sqlx::MySqlPool,
    blobs: &BlobCounts,
) -> Result<ShardBlobCounts, sqlx::Error> {
    //Do stuff
  Ok(some_data)
}

async fn get_blobs_from_table(
    pool: &sqlx::MySqlPool,
    blobs: &BlobCounts,
    table_name: &str,
) -> Result<ShardBlobCounts, sqlx::Error> {
   //Do stuff
  Ok(some_other_data)
}

Thanks in advance!

Every async function generates a different, unique type. They are not the Future type, but a secret unnamable type that happens to support Future's methods.

It doesn't matter that these functions asynchronously all return the same Result type, because the Future object you get from calling them describes every single step these functions will do internally, not just what they'll return.

This means you can't put mixed types in a Vec, the same way you can't mix integers and strings.

You can use Box::pin(call_async_function()) to have dynamic dispatch that adds a layer of abstraction that makes all future types the same, and these can be put in a Vec.

Alternatively, you can use join! macro that generates type-specific code that waits for a number of futures.

futures::join! works but I wanted a vector because I don't know a priori how many futures I need. I've tried Box::pin and Box::new but both give the same opaque type error. Probably I'm doing something wrong?

let futures = vec![
        Box::pin(get_blobs_from_data(pool, &blobs)),
        Box::pin(get_blobs_from_table(pool, &blobs, "TestTable")),
    ];

You need to cast the pinned box to a trait object, because you want Pin<Box<dyn Future>> not Pin<Box<SomeOpaqueFutureType>>.

let futures = vec![
    Box::pin(get_blobs_from_data(pool, &blobs)) as Pin<Box<dyn Future<Output = Foo>>>,
    // casting the first is enough. The second will be converted due to
    // type inference.
    Box::pin(get_blobs_from_table(pool, &blobs, "TestTable")),
];

or use FutureExt::boxed

let futures = vec![
    get_blobs_from_data(pool, &blobs).boxed(),
    get_blobs_from_table(pool, &blobs, "TestTable").boxed(),
];
2 Likes

Nesting is unnecessary, join! is variadic and will take however many futures you give it. (There is also the join function which only takes 2).

1 Like

The join! macro still requires you to know the number of futures up-front.

Many thanks! That's exactly what I needed, the solution and the explanation :+1:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.