Shared state across multiple threads in Actix-web
Let’s look at “state” in Actix-web
HttpRequest::app_data
app_data
is a method provided by actix-web
, specifically for the App
and HttpRequest
types. Here’s how it works:
- This method is used when setting up your
App
to store shared application state. You call it during the application configuration to register data that will be accessible across all routes and handlers. - Example:
rust App::new() .app_data(Data::new(Mutex::new(MyData { counter: 0 })))
- By calling
app_data
, you store a piece of data (likeData<Mutex<MyData>>
) that can be shared across all incoming requests. Internally,actix-web
stores this data in a way that makes it accessible throughout the application’s lifetime.
- This method is used within a handler to retrieve the application data you stored earlier using
App::app_data
. - It allows you to access shared state, and it returns an
Option<&T>
, whereT
is the type you registered.
rust async fn index(req: HttpRequest) -> impl Responder {
let data = req.app_data::<Data<Mutex<MyData>>>().unwrap();
let mut my_data = data.lock().unwrap(); my_data.counter += 1;
HttpResponse::Ok().body(format!("Counter: {}\n", my_data.counter)) }
Summary:
App::app_data
is used to set up shared state during application initialization.HttpRequest::app_data
is used to access that shared state within request handlers.
These methods help in managing and sharing data across different parts of youractix-web
application.
Example code
use std::sync::Mutex;
use actix_web::{App, HttpRequest, HttpResponse, Responder, HttpServer, web::{self, Data}};
struct MyData {
counter: usize,
}
/// Use the `Data<T>` extractor to access data in a handler.
async fn index(data: Data<Mutex<MyData>>) -> impl Responder {
let mut my_data = data.lock().unwrap();
my_data.counter += 1;
HttpResponse::Ok().body(format!("Counter: {}\n", my_data.counter))
}
Alternatively, use the HttpRequest::app_data
method to access data in a handler.
/// Alternatively, use the `HttpRequest::app_data` method to access data in a handler.
async fn index_alt(req: HttpRequest) -> impl Responder {
let data = req.app_data::<Data<Mutex<MyData>>>().unwrap();
let mut my_data = data.lock().unwrap();
my_data.counter += 1;
HttpResponse::Ok().body(format!("Counter: {}\n", my_data.counter))
}
And here is the main function
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let data = Data::new(Mutex::new(MyData { counter: 0 }));
HttpServer::new(move || {
// Move data into the closure
App::new()
// Store `MyData` in application storage.
.app_data(Data::clone(&data))
.route("/index.html", web::get().to(index))
.route("/index-alt.html", web::get().to(index_alt))
})
.bind(("127.0.0.1", 8080))? // Bind to 127.0.0.1:8080
.run()
.await
}
The line Data::clone(&data)
is effectively cloning a reference to the Data<Mutex<MyData>>
instance.
~/rust/hello-world main*
❯ curl http://127.0.0.1:8080/alt_index.html
~/rust/hello-world main*
❯ curl http://127.0.0.1:8080/index-alt.html
Counter: 1
~/rust/hello-world main*
❯ curl http://127.0.0.1:8080/index.html
Counter: 2
~/rust/hello-world main*
❯ curl http://127.0.0.1:8080/index-alt.html
Counter: 3
~/rust/hello-world main*
❯ curl http://127.0.0.1:8080/index-alt.html
Counter: 4
~/rust/hello-world main*
❯ curl http://127.0.0.1:8080/index.html
Counter: 5