diff --git a/content/docs/application.md b/content/docs/application.md index 95df909..4ba4f27 100644 --- a/content/docs/application.md +++ b/content/docs/application.md @@ -43,30 +43,33 @@ as the first application, it would match all incoming requests. ## State Application state is shared with all routes and resources within the same scope. State -can be accessed with the `web::Data` extractor as read-only, but interior mutability with -`Cell` can be used to achieve state mutability. State is also available for route -matching guards and middlewares. +can be accessed with the `web::Data` extractor. State is also available for route matching guards and middlewares. -Let's write a simple application that uses shared state. We are going to store request count -in the state: +Let's write a simple application and store the application name in the state: {{< include-example example="application" file="state.rs" section="setup" >}} -When the app is initialized it needs to be passed the initial state: - -{{< include-example example="application" file="state.rs" section="make_app" >}} - -> **Note**: `HttpServer` accepts an application factory rather than an application -> instance. `HttpServer` constructs an application instance for each thread, thus -> application state must be constructed multiple times. If you want to share state between -> different threads, a shared object should be used, e.g. `Arc`. There is also an -> [Example][stateexample] using `Arc` for this. Application state does not need to be -> `Send` and `Sync`, but the application factory must be `Send` + `Sync`. - -To start the previous app, create it into a closure: +and pass in the state when initializing the App, and start the application: {{< include-example example="application" file="state.rs" section="start_app" >}} +## Shared Mutable State + +`HttpServer` accepts an application factory rather than an application instance. +Http server constructs an application instance for each thread, thus 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. + +Internally, `web::Data` uses Arc. Thus, in order to avoid double Arc, we should create our Data before registering it using `register_data()`. + +In the following example, we will write an application with mutable, shared state. First, we define our state and create our handler: + +{{< include-example example="application" file="state.rs" section="setup_mutable" >}} + +and register the data in an App: + +{{< include-example example="application" file="state.rs" section="make_app_mutable" >}} + ## Combining applications with different state Combining multiple applications with different state is possible as well. diff --git a/examples/application/src/state.rs b/examples/application/src/state.rs index 6f5c278..7138c39 100644 --- a/examples/application/src/state.rs +++ b/examples/application/src/state.rs @@ -1,36 +1,48 @@ // use actix_web::{web, App, HttpServer}; -use std::cell::Cell; +use std::sync::{Mutex}; // This struct represents state struct AppState { - counter: Cell, + app_name: String, } fn index(data: web::Data) -> String { - let count = data.counter.get() + 1; // <- get count - data.counter.set(count); // <- store new count in state + let app_name = data.app_name; // <- get app_name - format!("Request number: {}", count) // <- response with count + format!("Hello {}!", app_name) // <- response with app_name } // -// +// +struct AppStateWithCounter { + counter: Mutex, // <- Mutex is necessary to mutate safely across threads +} + +fn _index(data: web::Data) -> String { + let mut counter = data.counter.lock().unwrap(); // <- get counter's MutexGuard + *counter += 1; // <- access counter inside MutexGuard + + format!("Request number: {}", counter) // <- response with count +} +// + +// fn _main() { + let counter = web::Data::new(AppStateWithCounter { counter : Mutex::new(0)}); + App::new() - .data(AppState { - counter: Cell::new(0), - }) + .register_data(counter.clone()) // <- register the created data .route("/", web::get().to(index)); } -// +// // pub fn main() { HttpServer::new(|| { App::new() .data(AppState { - counter: Cell::new(0), + app_name: String::from("Actix-web") }) .route("/", web::get().to(index)) })