liveBook · Manning

State

As explained in Rust Servers, Services, and Apps

Application state is the data representing the current condition of an application's configuration, behavior, or resources.

Application state holds data that describes an application’s current condition. In a REST service, that includes resource data: adding or removing a course changes the service’s state. State can also hold shared dependencies, such as a database connection pool. The course service first keeps its resources in memory, then moves them to Postgres. section 3.2.2 section 4.3.2

An Actix server runs application instances on multiple worker threads; their data is not shared automatically. Define an `AppState` in `state.rs`, construct it once with `web::Data::new(...)`, and attach clones of that shared data to the app with `.app_data(shared_data.clone())`. A handler receives it as a `web::Data<AppState>` parameter. section 3.2.2

Start with a health check

Give `AppState` a `health_check_response: String` and `visit_count: Mutex<u32>`. The `/health` handler reads the string, locks the count, includes its current value in the response, and increments it. The string illustrates shared immutable data; the mutex protects a value that requests mutate. section 3.2.2

Listing 3.3 Building an Actix web server with application state
use actix_web::{web, App, HttpServer};
use std::io;
use std::sync::Mutex;
 
#[path = "../handlers.rs"]
mod handlers;
#[path = "../routes.rs"]
mod routes;
#[path = "../state.rs"]
mod state;
 
use routes::*;
use state::AppState;
 
#[actix_rt::main]
async fn main() -> io::Result<()> {
  let shared_data = web::Data::new(AppState {                              #1
    health_check_response: "I'm good. You've already asked me ".to_string(),
    visit_count: Mutex::new(0),
  });
  let app = move || {                                                      #2
      App::new()
          .app_data(shared_data.clone())                                   #3
          .configure(general_routes)                                       #4
  };
 
  HttpServer::new(app).bind("127.0.0.1:3000")?.run().await                 #5
}

Shows where the state is initialized and attached to the Actix application. section 3.2.2

For the in-memory course API, add `courses: Mutex<Vec<Course>>` to `AppState` and initialize it with an empty vector in `bin/tutor-service.rs`. Keep the `Course` data model in `models.rs`, route definitions in `routes.rs`, and request logic in `handlers.rs`. This version uses the shared collection instead of a persistent data store. section 3.2.1 section 3.2.3

The course handlers put that state to work:

  • A POST to `/courses/` supplies JSON course data. The handler receives `AppState`, counts existing courses for that tutor, creates the new course, and stores it in the shared collection. section 3.2.4
  • A GET to `/courses/{tutor_id}` reads the collection, filters it by the path parameter, and returns the matching courses as JSON. section 3.2.5

When courses move to Postgres, change what state carries: retain the health-check fields, but replace the in-memory course collection with `db: PgPool`. In `main()`, read `DATABASE_URL`, create the sqlx pool, place it in `AppState`, and inject the state into Actix. A course handler extracts `app_state.db`, passes the pool and request parameters to a database-access function, and turns its result into an HTTP response. Resource data now lives in the database; state supplies the shared means of accessing it. section 4.3.2 section 4.3.3 section 4.4.2

use sqlx::postgres::PgPool;
use std::sync::Mutex;
pub struct AppState {
  pub health_check_response: String,
  pub visit_count: Mutex<u32>,
  pub db: PgPool,
}

Shows the database-backed `AppState` fields after the in-memory course collection is replaced by a Postgres pool. section 4.3.2

Tests can call handlers directly by constructing `web::Data<AppState>` and the required request arguments themselves. Initialize an empty course collection for an in-memory test, or create a test pool for a database-backed one. Check more than an unconditional success response when testing real state changes: skeletal handlers can pass status-code tests before database access exists. section 3.2.4 section 4.3.4

How Rust Servers, Services, and Apps covers it

How deep it goes
Builds and tests in-memory state, then replaces its course collection with an injected Postgres pool.
Examples
Uses a health-check visit counter, course POST and GET handlers, and tests that construct handler state.
What it assumes
Introduces Actix state sharing with a simple health check before adding course resources and database access.
What you can do after
Build it from scratch with Actix `web::Data`, then configure handlers to use shared collections or a sqlx pool.
How it connects
Connects REST resources, Actix handlers, worker threads, mutexes, dependency injection, and database connection pooling.

Also explained in

Compare how 39 books explain state →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Rust Servers, Services, and Apps. The section links point to the parts of the book it draws on.