22

Can anyone help me understand why this code compiles fine:

use actix_web::{App, HttpServer};
use anyhow::Result;

mod error;

#[actix_rt::main]
async fn main() -> Result<()> {
    HttpServer::new(|| App::new())
        .bind("127.0.0.1:8080")?
        .run()
        .await?;

    Ok(())
}

while this doesn't compile:

use actix_web::{App, HttpServer};
use anyhow::Result;

mod error;

#[actix_rt::main]
async fn main() -> Result<()> {
    HttpServer::new(|| App::new())
        .bind("127.0.0.1:8080")?
        .run()
        .await
}

with the error:

error[E0308]: mismatched types
 --> src/main.rs:6:1
  |
6 | #[actix_rt::main]
  | ^^^^^^^^^^^^^^^^^ expected struct `anyhow::Error`, found struct `std::io::Error`
7 | async fn main() -> Result<()> {
  |                    ---------- expected `std::result::Result<(), anyhow::Error>` because of return type
  |
  = note: expected enum `std::result::Result<_, anyhow::Error>`
             found enum `std::result::Result<_, std::io::Error>`
  = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

What is fundamentally different between the two examples?

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Zizheng Tai
  • 6,170
  • 28
  • 79

3 Answers3

20

The problem is that the second example is returning Result<(), std::io::Error> which is returned by starting the server, whereas the first one is returning Result<(), anyhow::Error> in the form of Ok(()).

The reason the first example works is that the ? operator (or the try! macro) performs a conversion from whatever error is returned to the error of the return type of the function.

From the docs:

In case of the Err variant, it retrieves the inner error. try! then performs conversion using From. This provides automatic conversion between specialized errors and more general ones.

Bennett Hardwick
  • 1,359
  • 9
  • 16
14

You can use the same conversion that anyhow uses:

use actix_web::{App, HttpServer};
use anyhow::Result;

#[actix_rt::main]
async fn main() -> Result<()> {
    HttpServer::new(|| App::new())
        .bind("127.0.0.1:8080")?
        .run()
        .await.map_err(anyhow::Error::from)
}
Caleb Hattingh
  • 9,005
  • 2
  • 31
  • 44
4

anyhow::Error implements From<std::error::Error>, but they are not the same thing.

In the first one the ? is taking the std::error::Error from run().await and calling anyhow::Error::from. The second one is not.

Changing the return type of your method to

Result<(), std::error::Error>

fixes the issue.

pigeonhands
  • 3,066
  • 15
  • 26