pub struct State<T>(pub T);Expand description
Extract shared application state
This extractor provides access to the shared application state that was
provided when creating the router. The state is automatically cloned for
each request, avoiding the need to manage Arc<Mutex<T>> manually.
§Database Connection Patterns
The state system is designed to work seamlessly with database connection pools. Since state is cloned for each request, database connections should use connection pools wrapped in Arc for efficient sharing.
§PostgreSQL Example (using sqlx)
ⓘ
use coapum::extract::State;
use std::sync::Arc;
#[derive(Clone)]
struct AppState {
db: Arc<sqlx::PgPool>,
cache: Arc<tokio::sync::RwLock<std::collections::HashMap<String, String>>>,
}
async fn get_user_handler(State(state): State<AppState>) -> Result<String, Box<dyn std::error::Error>> {
let user = sqlx::query!("SELECT name FROM users WHERE id = $1", 1)
.fetch_one(&*state.db)
.await?;
Ok(user.name)
}§SQLite Example (using sqlx)
ⓘ
use coapum::{Json, extract::State};
use std::sync::Arc;
#[derive(Clone)]
struct AppState {
db: Arc<sqlx::SqlitePool>,
config: AppConfig,
}
async fn insert_data_handler(
State(state): State<AppState>,
Json(data): Json<serde_json::Value>
) -> Result<(), Box<dyn std::error::Error>> {
sqlx::query!("INSERT INTO data (payload) VALUES (?)", data.to_string())
.execute(&*state.db)
.await?;
Ok(())
}§Diesel Example
ⓘ
use coapum::extract::State;
use std::sync::Arc;
use diesel::r2d2::{Pool, ConnectionManager};
use diesel::PgConnection;
type DbPool = Arc<Pool<ConnectionManager<PgConnection>>>;
#[derive(Clone)]
struct AppState {
db_pool: DbPool,
redis_client: Arc<redis::Client>,
}
async fn database_handler(State(state): State<AppState>) {
let mut conn = state.db_pool.get().expect("Failed to get connection");
// Use connection for database operations
}§Generic Database Pattern
For maximum flexibility, you can define a trait for database operations:
ⓘ
use async_trait::async_trait;
use coapum::extract::State;
use std::sync::Arc;
#[derive(Clone)]
struct User {
id: i32,
name: String,
}
#[derive(Clone)]
struct Cache {
// Cache implementation
}
#[async_trait]
pub trait DatabaseOps: Send + Sync + Clone {
type Error: std::error::Error + Send + Sync + 'static;
async fn get_user(&self, id: i32) -> Result<User, Self::Error>;
async fn save_data(&self, data: &serde_json::Value) -> Result<(), Self::Error>;
}
#[derive(Clone)]
struct AppState<DB: DatabaseOps> {
db: DB,
cache: Arc<tokio::sync::RwLock<Cache>>,
}
async fn generic_handler<DB: DatabaseOps>(
State(state): State<AppState<DB>>
) -> Result<(), DB::Error> {
let user = state.db.get_user(123).await?;
Ok(())
}§Best Practices for State Management
- Use Arc for shared resources: Database pools, configuration, caches
- Keep state lightweight: Large objects should be behind Arc
- Prefer connection pools: For database connections, always use pools
- Clone should be cheap: State is cloned per request, so make it efficient
- Consider read-heavy workloads: Use
Arc<RwLock<T>>for cached data that’s read frequently
§Basic Example
use coapum::extract::State;
#[derive(Clone)]
struct AppState {
database_url: String,
api_key: String,
}
async fn handle_with_state(State(state): State<AppState>) {
println!("Database: {}", state.database_url);
}Tuple Fields§
§0: TTrait Implementations§
Source§impl<T, S> FromRequest<S> for State<T>
impl<T, S> FromRequest<S> for State<T>
Source§type Rejection = StateRejection
type Rejection = StateRejection
The error type returned when extraction fails
Source§fn from_request<'life0, 'life1, 'async_trait>(
_req: &'life0 CoapumRequest<SocketAddr>,
state: &'life1 S,
) -> Pin<Box<dyn Future<Output = Result<Self, Self::Rejection>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn from_request<'life0, 'life1, 'async_trait>(
_req: &'life0 CoapumRequest<SocketAddr>,
state: &'life1 S,
) -> Pin<Box<dyn Future<Output = Result<Self, Self::Rejection>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Extract this type from the request
Auto Trait Implementations§
impl<T> Freeze for State<T>where
T: Freeze,
impl<T> RefUnwindSafe for State<T>where
T: RefUnwindSafe,
impl<T> Send for State<T>where
T: Send,
impl<T> Sync for State<T>where
T: Sync,
impl<T> Unpin for State<T>where
T: Unpin,
impl<T> UnwindSafe for State<T>where
T: UnwindSafe,
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more