Search code examples
rustactix-web

actix pass appliction state with string


Hello I want to pass the following AppState.

pub struct AppState {
    clients: Vec<Client>,
}

This is how I the server:

async fn launch_server(app_config: CmkClientServerConfig) -> std::io::Result<()> {
HttpServer::new(|| App::new()
    .data(Config::default().realm("Restricted area"))
    .data(AppState {
        clients: app_config.clients
    })
    .app_data(web::PayloadConfig::new(1000000))
    .service(web::resource("/").route(web::post().to(index))))
    .bind("127.0.0.1:8080")?
    .workers(1)
    .run()
    .await
}

The Client entity struct I want to pass:

#[derive(Serialize, Deserialize, Debug)]
pub struct Client {
    pub id: String,
    pub password: String,
}

I hope it is clear what I am trying to accomplish. Now to my Problem. Compiler says:

the trait bound entities::CmkClientServerConfig: std::clone::Clone is not satisfied in `[closure@src/server.rs:25:21: 31:66 app_config:entities::CmkClientServerConfig]

Maybe this thing needs to be cloned because of async execution. So added "#[derive(Clone)]" to CmkClientServerConfig struct and the Client struct.

Then a get this error message:

error[E0507]: cannot move out of `app_config.clients`, as `app_config` is a captured variable in an `Fn` closure
  --> src/server.rs:29:22
   |
23 | async fn launch_server(app_config: CmkClientServerConfig) -> std::io::Result<()> {
   |                        ---------- captured outer variable
...
29 |             clients: app_config.clients
   |                      ^^^^^^^^^^^^^^^^^^ move occurs because `app_config.clients` has type `std::vec::Vec<entities::Client>`, which does not implement the `Copy` trait

error[E0507]: cannot move out of `app_config.clients`, as `app_config` is a captured variable in an `Fn` closure
  --> src/server.rs:29:22
   |
23 | async fn launch_server(app_config: CmkClientServerConfig) -> std::io::Result<()> {
   |                        ---------- captured outer variable
...
29 |             clients: app_config.clients
   |                      ^^^^^^^^^^^^^^^^^^ move occurs because `app_config.clients` has type `std::vec::Vec<entities::Client>`, which does not implement the `Copy` trait

My question is how to do it correctly, how can I make a clone possible.

Thanks for your help.


Solution

  • Both the API docs and the Get Started docs explain:

    HttpServer accepts an application factory rather than an application instance. An HttpServer constructs an application instance for each thread. Therefore, application data must be constructed multiple times. If you want to share data between different threads, a shareable object should be used, e.g. Send + Sync.

    If your clients list is only initialized on server start and otherwise stays immutable then you can just clone it in your app factory function, but if it's suppose to be mutable then you must wrap it in a Arc<Mutex<T>> so that it can safely be cloned, shared, and mutated across the multiple threads running your app.

    If the clients list does not change then update your client to derive the Clone trait:

    #[derive(Serialize, Deserialize, Debug, Clone)] // Clone added here
    pub struct Client {
        pub id: String,
        pub password: String,
    }
    

    And then update your app factory function to move your app_config into itself so you can repeatedly call clone on clients:

    async fn launch_server(app_config: CmkClientServerConfig) -> std::io::Result<()> {
        HttpServer::new(move || App::new() // move added here
            .data(Config::default().realm("Restricted area"))
            .data(AppState {
                clients: app_config.clients.clone() // clone() added here
            })
            .app_data(web::PayloadConfig::new(1000000))
            .service(web::resource("/").route(web::post().to(index))))
            .bind("127.0.0.1:8080")?
            .workers(1)
            .run()
            .await
    }