Trait objects in actix appliaction states

Hi,

I am trying to pass a trait object to actix web:

trait Service {
    fn bar(&self);
}

struct Mock;

impl Service for Mock {
    fn bar(&self) {}
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let service = Mock{};

    let arc_service: Arc<dyn Service> = Arc::new(service);
    let data_service: web::Data<dyn Service> = web::Data::from(arc_service);
    
    HttpServer::new(move || {
        App::new()
            .app_data(data_service.clone())
    })
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
}

@cc-ord figured out the same solution but unfortunately it i cannot get it work and getting a thread-safety error:

error[E0277]: `dyn Service` cannot be shared between threads safely
  --> src/main.rs:48:21
   |
48 |       HttpServer::new(move || {
   |  _____---------------_^
   | |     |
   | |     required by a bound introduced by this call
49 | |         App::new()
50 | |             .app_data(data_service.clone())
51 | |     })
   | |_____^ `dyn Service` cannot be shared between threads safely
   |
   = help: the trait `Sync` is not implemented for `dyn Service`
   = note: required for `Arc<dyn Service>` to implement `Send`
   = note: required because it appears within the type `actix_web::web::Data<dyn Service>`
note: required because it's used within this closure
  --> src/main.rs:48:21
   |
48 |     HttpServer::new(move || {
   |                     ^^^^^^^
note: required by a bound in `HttpServer::<F, I, S, B>::new`
  --> /home/dts/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-web-4.3.0/src/server.rs:84:20
   |
84 |     F: Fn() -> I + Send + Clone + 'static,
   |                    ^^^^ required by this bound in `HttpServer::<F, I, S, B>::new`

Does anyone has an idea how to solve that (i think a very common) use case?

try adding the Send and Sync markers explicitly:

let arc_service: Arc<dyn Service + Send + Sync> = Arc::new(service);
let data_service: web::Data<dyn Service + Send + Sync> = web::Data::from(arc_service);

This removes the compile error in your minimal example, but I have no idea if you can extract the dyn Service from Data<_> in your routes, as I never tried something like that before.

YES!! thank you thousand times (i am new to rust and was stuck here :wink: )

but I have no idea if you can extract the dyn Service from Data<_> in your routes

i can still use the web::Data Extractor as long as i use the exact same type info:

#[get("/foo")]
async fn foo(service: web::Data<dyn Service + Send + Sync> ) -> HttpResponse { todo!() }
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.