Confusion about opaque types error using async and anyhow::Result

I've got a list of async functions that all return anyhow::Result<Foo>, and I'd like to run the concurrently, using futures::future::join_all to wait for them.

Errors for each can differ, which is why I tried wrapping them using anyhow, but I'm getting a confusing error message about opaque types when I try and compile.

Minimal example (Rust Playground):

use futures::future::join_all;

async fn a() -> anyhow::Result<u32> { Ok(23) }

async fn b() -> anyhow::Result<u32> { Ok(14) }

#[tokio::main]
async fn main() {
    let tasks = vec![a(), b()];
    dbg!(join_all(tasks).await);
}

this fails to compile with:

error[E0308]: mismatched types
 --> src/main.rs:9:27
  |
9 |     let tasks = vec![a(), b()];
  |                           ^^^ expected opaque type, found a different opaque type
  |
note: while checking the return type of the `async fn`
 --> src/main.rs:3:17
  |
3 | async fn a() -> anyhow::Result<u32> { Ok(23) }
  |                 ^^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, expected opaque type
note: while checking the return type of the `async fn`
 --> src/main.rs:5:17
  |
5 | async fn b() -> anyhow::Result<u32> { Ok(14) }
  |                 ^^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, found opaque type
  = note: expected opaque type `impl futures::Future<Output = Result<u32, anyhow::Error>>` (opaque type at <src/main.rs:3:17>)
             found opaque type `impl futures::Future<Output = Result<u32, anyhow::Error>>` (opaque type at <src/main.rs:5:17>)
  = help: consider `await`ing on both `Future`s
  = note: distinct uses of `impl Trait` result in different opaque types

For more information about this error, try `rustc --explain E0308`.

Can anyone help clarify the problem here? I'm guessing that the problem is that anyhow uses impl Trait causing the return types of the different functions to differ?

Any suggested alternative approach for writing this? In my actual code, each function wraps quite different error types (e.g. some are performing HTTP requests, some are making DB queries) - I can create a new error type to convert them all into, implement from/into etc., but was hoping to use anyhow to avoid that.

async fn foo(...) -> Result<...> is syntax sugar for fn foo(...) -> impl Future<Output=Result<...>>. The return value of foo() is not a Result, but an opaque type that implements the Future trait, and this is what's causing the error you're seeing.

You have two easy choices here:

  1. Use trait objects (BoxFuture, for example), to hide the opaque type behind a trait object (dyn Future<Output=Result<...>> instead of impl Future...
  2. Instead of join_all, use join! which can handle different types.

Which is right depends on your underlying problem.

1 Like

Thanks, the BoxFuture approach worked great here (there's an variable number of functions being run, so join didn't work out here).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.