pub struct AppState { /* private fields */ }Expand description
A type-safe container for shared application state.
AppState allows you to store multiple different types of state in a single
container. Each type is identified by its TypeId, ensuring type safety when
retrieving state.
§Thread Safety
AppState is fully thread-safe and can be cloned cheaply (uses Arc internally).
Multiple handlers can access the same state concurrently without additional
synchronization.
§Memory Management
State is stored using Arc, so cloning AppState or extracting state with
the State extractor only increments a reference count. The actual state
data is shared across all references.
§Type Requirements
Types stored in AppState must be:
Send: Can be sent between threadsSync: Can be referenced from multiple threads'static: Has a static lifetime
§Examples
§Creating and Using State
use wsforge::prelude::*;
use std::sync::Arc;
// Create empty state
let state = AppState::new();
// Add some data
state.insert(Arc::new("Hello".to_string()));
state.insert(Arc::new(42_u32));
// Retrieve data
let text: Option<Arc<String>> = state.get();
assert_eq!(*text.unwrap(), "Hello");
let number: Option<Arc<u32>> = state.get();
assert_eq!(*number.unwrap(), 42);§With Router
use wsforge::prelude::*;
use std::sync::Arc;
struct AppConfig {
name: String,
version: String,
}
async fn info_handler(State(config): State<Arc<AppConfig>>) -> Result<String> {
Ok(format!("{} v{}", config.name, config.version))
}
let config = Arc::new(AppConfig {
name: "MyApp".to_string(),
version: "1.0.0".to_string(),
});
let router = Router::new()
.with_state(config)
.default_handler(handler(info_handler));§Complex State Management
use wsforge::prelude::*;
use std::sync::Arc;
use std::collections::HashMap;
struct UserStore {
users: tokio::sync::RwLock<HashMap<u64, String>>,
}
impl UserStore {
fn new() -> Self {
Self {
users: tokio::sync::RwLock::new(HashMap::new()),
}
}
async fn add_user(&self, id: u64, name: String) {
self.users.write().await.insert(id, name);
}
async fn get_user(&self, id: u64) -> Option<String> {
self.users.read().await.get(&id).cloned()
}
}
async fn user_handler(
State(store): State<Arc<UserStore>>,
) -> Result<String> {
store.add_user(1, "Alice".to_string()).await;
let user = store.get_user(1).await;
Ok(format!("User: {:?}", user))
}Implementations§
Source§impl AppState
impl AppState
Sourcepub fn insert<T: Send + Sync + 'static>(&self, value: Arc<T>)
pub fn insert<T: Send + Sync + 'static>(&self, value: Arc<T>)
Inserts a value into the state.
If a value of the same type already exists, it will be replaced.
The value is automatically wrapped in an Arc.
§Type Requirements
The type T must implement:
Send: Can be transferred across thread boundariesSync: Can be safely shared between threads'static: Has a static lifetime (no borrowed data)
§Arguments
value- The value to store in state
§Examples
§Basic Usage
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
// Insert different types
state.insert(Arc::new(String::from("Hello")));
state.insert(Arc::new(42_u32));
state.insert(Arc::new(true));§Replacing Values
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(10_u32));
assert_eq!(*state.get::<u32>().unwrap(), 10);
// Replace with new value
state.insert(Arc::new(20_u32));
assert_eq!(*state.get::<u32>().unwrap(), 20);§Custom Types
use wsforge::prelude::*;
use std::sync::Arc;
struct Database {
url: String,
}
let state = AppState::new();
let db = Arc::new(Database {
url: "postgres://localhost/mydb".to_string(),
});
state.insert(db);Sourcepub fn get<T: Send + Sync + 'static>(&self) -> Option<Arc<T>>
pub fn get<T: Send + Sync + 'static>(&self) -> Option<Arc<T>>
Retrieves a value from the state by its type.
Returns None if no value of type T has been stored.
Returns Some(Arc<T>) if a value exists.
§Type Safety
The returned value is guaranteed to be of type T because values
are stored and retrieved using TypeId.
§Performance
This operation is O(1) and lock-free, making it very efficient even with concurrent access from multiple threads.
§Examples
§Basic Retrieval
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(String::from("Hello")));
let text: Option<Arc<String>> = state.get();
assert_eq!(*text.unwrap(), "Hello");
// Trying to get a type that doesn't exist
let number: Option<Arc<u32>> = state.get();
assert!(number.is_none());§Pattern Matching
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(42_u32));
match state.get::<u32>() {
Some(value) => println!("Found: {}", value),
None => println!("Not found"),
}§Multiple Types
use wsforge::prelude::*;
use std::sync::Arc;
struct Config { port: u16 }
struct Database { url: String }
let state = AppState::new();
state.insert(Arc::new(Config { port: 8080 }));
state.insert(Arc::new(Database { url: "...".to_string() }));
// Each type is stored separately
let config: Arc<Config> = state.get().unwrap();
let db: Arc<Database> = state.get().unwrap();
println!("Port: {}, DB: {}", config.port, db.url);§With Error Handling
use wsforge::prelude::*;
use std::sync::Arc;
struct Database;
let state = AppState::new();
let db = state
.get::<Database>()
.ok_or_else(|| Error::custom("Database not configured"))?;
// Use db...Sourcepub fn contains<T: Send + Sync + 'static>(&self) -> bool
pub fn contains<T: Send + Sync + 'static>(&self) -> bool
Checks if a value of type T exists in the state.
This is equivalent to state.get::<T>().is_some() but more explicit.
§Examples
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(42_u32));
assert!(state.contains::<u32>());
assert!(!state.contains::<String>());Sourcepub fn remove<T: Send + Sync + 'static>(&self) -> Option<Arc<T>>
pub fn remove<T: Send + Sync + 'static>(&self) -> Option<Arc<T>>
Removes a value of type T from the state.
Returns the removed value if it existed, or None otherwise.
§Examples
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(42_u32));
assert!(state.contains::<u32>());
let value = state.remove::<u32>();
assert_eq!(*value.unwrap(), 42);
assert!(!state.contains::<u32>());Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of different types stored in the state.
§Examples
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
assert_eq!(state.len(), 0);
state.insert(Arc::new(String::from("Hello")));
assert_eq!(state.len(), 1);
state.insert(Arc::new(42_u32));
assert_eq!(state.len(), 2);
// Replacing same type doesn't increase count
state.insert(Arc::new(100_u32));
assert_eq!(state.len(), 2);Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Checks if the state is empty (contains no data).
§Examples
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
assert!(state.is_empty());
state.insert(Arc::new(42_u32));
assert!(!state.is_empty());Sourcepub fn clear(&self)
pub fn clear(&self)
Clears all state data.
Removes all stored values, leaving the state empty.
§Examples
use wsforge::prelude::*;
use std::sync::Arc;
let state = AppState::new();
state.insert(Arc::new(String::from("Hello")));
state.insert(Arc::new(42_u32));
assert_eq!(state.len(), 2);
state.clear();
assert_eq!(state.len(), 0);
assert!(state.is_empty());