dtolnay / async-trait

Type erasure for async trait methods
Apache License 2.0
1.81k stars 84 forks source link

[question] Why some code compile error? #139

Closed fawdlstty closed 3 years ago

fawdlstty commented 3 years ago

I had a compiler crash while compiling a piece of code #79783, after fix the bug, I found that the same piece of code failed to compile after using async-trait. Example:

struct A {}
impl A {
    async fn xx_test (&self) {
        match actix_web::HttpServer::new (|| {
            actix_web::App::new ()
        }).bind ("127.0.0.1:8080") {
            Ok (mut _server) => { _server.run ().await; () },
            Err (_e) => println! ("http listen failed: {}", _e.to_string ()),
        }
    }
}

#[async_trait]
pub trait B: Send + Sync {
    async fn test_func (&self);
}
struct C {}
#[async_trait]
impl B for C {
    async fn test_func (&self) {
        // compile error
        match actix_web::HttpServer::new (|| {
            actix_web::App::new ()
        }).bind ("127.0.0.1:8080") {
            Ok (mut _server) => { _server.run ().await; () },
            Err (_e) => println! ("http listen failed: {}", _e.to_string ()),
        }
    }
}

Why is that? How to fix it?

fawdlstty commented 3 years ago

If I use the #[async_trait(?Send)], it will compile and pass, but the trait won't Send, and I don't want that

dtolnay commented 3 years ago

I'll close this since your question is why actix-web results in non-Send futures, not anything about async-trait.

You can see in the error from this code that the future is non-Send because of what's stored in HttpServer:

use actix_web::{App, HttpServer};

async fn repro() {
    match HttpServer::new(App::new).bind("127.0.0.1:8080") {
        Ok(server) => { let _ = server.run().await; }
        Err(err) => eprintln!("error: {}", err),
    }
}

fn main() {
    fn assert_send(_: impl Send) {}
    assert_send(repro());
}
error: future cannot be sent between threads safely
  --> src/main.rs:12:5
   |
11 |     fn assert_send(_: impl Send) {}
   |                            ---- required by this bound in `assert_send`
12 |     assert_send(repro());
   |     ^^^^^^^^^^^ future returned by `repro` is not `Send`
   |
   = help: the trait `std::marker::Send` is not implemented for `(dyn std::any::Any + 'static)`
note: future is not `Send` as this value is used across an await
  --> src/main.rs:5:33
   |
4  |     match HttpServer::new(App::new).bind("127.0.0.1:8080") {
   |           ------------------------- has type `HttpServer<fn() -> App<actix_web::app_service::AppEntry, actix_web::dev::Body> {App::<actix_web::app_service::AppEntry, actix_web::dev::Body>::new}, App<actix_web::app_service::AppEntry, actix_web::dev::Body>, actix_web::app_service::AppInit<actix_web::app_service::AppEntry, actix_web::dev::Body>, actix_web::dev::Body>` which is not `Send`
5  |         Ok(server) => { let _ = server.run().await; }
   |                                 ^^^^^^^^^^^^^^^^^^ await occurs here, with `HttpServer::new(App::new)` maybe used later
...
8  | }
   | - `HttpServer::new(App::new)` is later dropped here
fawdlstty commented 3 years ago

So I should use send_wrapper, right? Thank you