pub struct LocalKeyImmutable<T: 'static>(/* private fields */);Expand description
An immutable task-local storage key.
This type provides task-local storage that cannot be modified once set within
a scope, offering stronger guarantees than LocalKey about value stability.
§Immutability Semantics
The term “immutable” here has specific semantics that may be counterintuitive:
- Within a scope: Once set via
scope(), the value cannot be changed byset()orreplace()methods (these methods don’t exist) - Across scopes: The same task-local can have different values in nested scopes, and a future may observe different values as it executes across scope boundaries
- Primary use case: Preventing downstream code from modifying configuration or context values that should remain stable
§Examples
task_local! {
static const CONFIG: String;
static MUTABLE_STATE: Vec<String>;
}
async fn demonstrate_immutability() {
CONFIG.scope("production".to_string(), async {
// This would not compile - no set() method:
// CONFIG.set("development".to_string());
// Access the immutable value
CONFIG.with(|cfg| {
assert_eq!(cfg.as_ref().unwrap().as_str(), "production");
});
// But MUTABLE_STATE can be modified:
MUTABLE_STATE.scope(vec![], async {
MUTABLE_STATE.with_mut(|v| {
if let Some(vec) = v {
vec.push("log".to_string());
}
});
}).await;
}).await;
}§When to Use
Use LocalKeyImmutable for:
- Request IDs, trace IDs, and correlation identifiers
- User context and authentication information
- Environment configuration
- Any value that should remain constant within a scope
Use regular LocalKey for:
- Accumulators and counters
- Mutable state that changes during execution
- Caches and temporary storage
Implementations§
Source§impl<T: 'static> LocalKeyImmutable<T>
impl<T: 'static> LocalKeyImmutable<T>
Sourcepub fn scope<F>(
&'static self,
value: T,
f: F,
) -> impl Future<Output = F::Output>
pub fn scope<F>( &'static self, value: T, f: F, ) -> impl Future<Output = F::Output>
Sets the immutable task-local value for the duration of a future.
Unlike LocalKey::scope, the value cannot be modified once set
within the scope. This is useful for configuration values that should
remain constant during execution.
§Examples
task_local! {
static const ENVIRONMENT: String;
}
async fn log_with_env(message: &str) {
ENVIRONMENT.with(|env| {
println!("[{}] {}", env.unwrap(), message);
});
}
async fn main_task() {
ENVIRONMENT.scope("production".to_string(), async {
log_with_env("Starting server").await;
// Unlike mutable task-locals, this would not compile:
// ENVIRONMENT.set("development".to_string()); // Error!
ENVIRONMENT.scope("staging".to_string(), async {
log_with_env("In staging context").await;
}).await;
log_with_env("Back in production").await;
}).await;
}Sourcepub fn get(&'static self) -> Twhere
T: Copy,
pub fn get(&'static self) -> Twhere
T: Copy,
Returns a copy of the immutable task-local value.
§Panics
Panics if the task-local value is not set.
§Examples
task_local! {
static const USER_ID: u64;
}
async fn get_user_permissions() -> Vec<String> {
let user_id = USER_ID.get();
// Fetch permissions for user_id...
vec![format!("read:user:{}", user_id)]
}
async fn example() {
USER_ID.scope(12345, async {
let perms = get_user_permissions().await;
assert_eq!(perms[0], "read:user:12345");
}).await;
}Sourcepub fn with<F, R>(&'static self, f: F) -> R
pub fn with<F, R>(&'static self, f: F) -> R
Accesses the immutable task-local value through a closure.
This is the safest way to access an immutable task-local value as it handles the case where the value might not be set.
§Examples
task_local! {
static const REQUEST_ID: String;
}
fn log_with_request_id(message: &str) {
REQUEST_ID.with(|id| {
match id {
Some(request_id) => println!("[{}] {}", request_id, message),
None => println!("[no-request-id] {}", message),
}
});
}
async fn handle_request() {
log_with_request_id("Processing request");
}
async fn example() {
// Without request ID
handle_request().await;
// With request ID
REQUEST_ID.scope("req-123".to_string(), async {
handle_request().await;
}).await;
}