[future, tokio] E0277 when using async block vs without or Try not implemented for Unit

Maybe I'm not understanding the difference in syntax, but when I use async {} in my program, my attempts to use x.await.map_err(|e| CustomError(e))? complains that .map_err cannot be called on a unit type ().

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
  --> src/main.rs:13:21
   |
13 |         let _conn = tokio::net::TcpListener::bind(&addr).await.map_err(|_err| "help")?;
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
   |
   = help: the trait `std::ops::Try` is not implemented for `()`
   = note: required by `std::ops::Try::from_error`
use std::error::Error;

#[tokio::main]
async fn main() {
    fails().await.unwrap();
    works().await.unwrap();
}

async fn fails() -> Result<(), Box<dyn Error>> {
    let addr = "127.0.0.1:8888".parse::<std::net::SocketAddr>()?;

    let server = async {
        let _conn = tokio::net::TcpListener::bind(&addr).await.map_err(|_err| "help")?;
    };

    server.await;

    Ok(())
}

async fn works() -> Result<(), Box<dyn Error>> {
    let addr = "127.0.0.1:8888".parse::<std::net::SocketAddr>()?;
    let _conn = tokio::net::TcpListener::bind(&addr).await.map_err(|_err| "help")?;

    Ok(())
}

Gist with Cargo.toml [1]

After reading the async book [2], both fails and works should effectively be identical, so why does fails generate an error that works does not?

[1]: https://gist.github.com/boxofrox/73583f1c2139483a37d82af716c84371
[2]: https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html

This is because the async block returns a jnkt type, because you don't return anything from the block.

Change the block to this to make it work,

let server = async {
    let _conn = tokio::net::TcpListener::bind(&addr).await.map_err(|_err| "help")?;
    Ok(())
};

Async blocks are more like closures than blocks.

Thank you. I'm complete crap at reading rustc errors sometimes. I thought the message was complaining about .await returning (). Why? Because the project I was trying to build a simple, concise example for had more subsequent code in the async block and reported...

error[E0599]: no method named `map_err` found for type `()` in the current scope
   --> src/bin/fuzzyd/main.rs:279:10
    |
278 |     let conn = tokio::net::TcpListener(&addr).await
279 |         .map_err(|e| Error::from(ErrorKind::AppTask("server shutdown", e)))?;
    |          ^^^^^^^ method not found in `()`
    |
    = note: the method `map_err` exists but the following trait bounds were not satisfied:
            `&() : futures_util::future::try_future::TryFutureExt`
            `&() : futures_util::stream::try_stream::TryStreamExt`
            `&mut () : futures_util::future::try_future::TryFutureExt`
            `&mut () : futures_util::stream::try_stream::TryStreamExt`
            `() : futures_util::future::try_future::TryFutureExt`
            `() : futures_util::stream::try_stream::TryStreamExt`

I had to add Ok(()) as Result<(), &'static str> to satisfy a lack of type inference, but the fails() function compiles.

Then I added Ok(()) to the bottom of my project's async block, and this E0599 error is gone. Hopefully never to return!

Cheers!

I think the error message is just bad in this case, it doesn't say anything about the async block which is where the error lies. Could you file a bug for this?

1 Like

I will, though it's low priority atm. Will try to knock out bug reports for each error by close of week.

1 Like

Well, E0277 seems to be sorted in rustc-1.41.0-nightly, and clearly mentions the async block as the source of the (). No bug report needed there.

error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
  --> src/lib.rs:13:21
   |
12 |       let server = async {
   |  ________________________-
13 | |         let _conn = tokio::net::TcpListener::bind(&addr).await.map_err(|_err| "help")?;
   | |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in an async block that returns `()`
14 | |     };
   | |_____- this function should return `Result` or `Option` to accept `?`
   |
   = help: the trait `std::ops::Try` is not implemented for `()`
   = note: required by `std::ops::Try::from_error`

I couldn't reproduce the error E0599 I initially got. Without a reproducible example, I'll skip the bug report on this for now. If I stumble upon it again, I'll pursue a bug report at that time.

Cheers.

2 Likes