pub struct Scope { /* private fields */ }Expand description
Scoped service container for request-scoped dependency resolution.
A Scope provides isolated dependency resolution for scoped services while
still accessing singleton services from the root provider. This is ideal for
web applications where you want request-scoped services (like database connections,
user contexts, etc.) that are shared within a single request but isolated
between requests.
§Lifetime Behavior
- Singleton: Resolved and cached in the root provider (shared across all scopes)
- Scoped: Resolved and cached within this specific scope
- Transient: Created fresh on every resolution (no caching)
§Examples
use ferrous_di::{ServiceCollection, Resolver};
use std::sync::{Arc, Mutex};
#[derive(Debug)]
struct DatabaseConnection(String);
#[derive(Debug)]
struct UserService {
db: Arc<DatabaseConnection>,
}
let mut collection = ServiceCollection::new();
// Scoped database connection per request
collection.add_scoped_factory::<DatabaseConnection, _>(|_| {
DatabaseConnection("connection-123".to_string())
});
// Transient user service that uses scoped DB connection
collection.add_transient_factory::<UserService, _>(|resolver| {
UserService {
db: resolver.get_required::<DatabaseConnection>(),
}
});
let provider = collection.build();
let scope = provider.create_scope();
// Multiple services in the same scope share the same DB connection
let user1 = scope.get_required::<UserService>();
let user2 = scope.get_required::<UserService>();
assert!(Arc::ptr_eq(&user1.db, &user2.db));Implementations§
Source§impl Scope
impl Scope
Sourcepub async fn dispose_all(&self)
pub async fn dispose_all(&self)
Disposes all scoped disposal hooks in LIFO order.
This method runs all asynchronous disposal hooks first (in reverse order), followed by all synchronous disposal hooks (in reverse order). This ensures proper cleanup of scoped services.
§Examples
use ferrous_di::{ServiceCollection, Dispose, Resolver};
use std::sync::Arc;
struct ScopedCache {
name: String,
}
impl Dispose for ScopedCache {
fn dispose(&self) {
println!("Disposing scoped cache: {}", self.name);
}
}
let mut services = ServiceCollection::new();
services.add_scoped_factory::<ScopedCache, _>(|r| {
let cache = Arc::new(ScopedCache { name: "request_cache".to_string() });
r.register_disposer(cache.clone());
ScopedCache { name: "request_cache".to_string() } // Return concrete type
});
let provider = services.build();
let scope = provider.create_scope();
// ... use scoped services ...
scope.dispose_all().await; // Only disposes scoped resourcesSourcepub async fn using<F, Fut, R, E>(&self, f: F) -> Result<R, E>
pub async fn using<F, Fut, R, E>(&self, f: F) -> Result<R, E>
Executes an async block with automatic disposal of services resolved via *_disposable methods.
This method provides a “using” pattern where services resolved with the disposable
variants (get_disposable, get_async_disposable, etc.) are automatically disposed
when the block exits, regardless of whether it succeeds or fails.
§Disposal Order
Services are disposed in LIFO order (last resolved, first disposed):
- Async disposers run first (in reverse order)
- Sync disposers run second (in reverse order)
§Error Handling
The block’s result is preserved even if disposal occurs. Disposal happens regardless of success or failure of the user block.
§Examples
use ferrous_di::{ServiceCollection, Dispose, AsyncDispose, DiError};
use async_trait::async_trait;
use std::sync::Arc;
struct DatabaseConnection;
impl Dispose for DatabaseConnection {
fn dispose(&self) {
println!("Closing database connection");
}
}
struct ApiClient;
#[async_trait]
impl AsyncDispose for ApiClient {
async fn dispose(&self) {
println!("Shutting down API client");
}
}
let mut services = ServiceCollection::new();
services.add_scoped_factory::<DatabaseConnection, _>(|_| DatabaseConnection);
services.add_scoped_factory::<ApiClient, _>(|_| ApiClient);
let provider = services.build();
let scope = provider.create_scope();
let result = scope.using(|resolver| async move {
let db = resolver.get_disposable::<DatabaseConnection>()?;
let api = resolver.get_async_disposable::<ApiClient>()?;
// Use the services...
Ok::<String, DiError>("Operation completed".to_string())
}).await?;
// Both services are automatically disposed here in LIFO order:
// 1. ApiClient.dispose() (async)
// 2. DatabaseConnection.dispose() (sync)
assert_eq!(result, "Operation completed");Sourcepub fn using_sync<F, R, E>(&self, f: F) -> Result<R, E>
pub fn using_sync<F, R, E>(&self, f: F) -> Result<R, E>
Executes a synchronous block with automatic disposal of services resolved via *_disposable methods.
This is the synchronous variant of using for blocks that don’t need async.
Only synchronous disposers are supported - async disposers will be ignored in this method.
§Examples
use ferrous_di::{ServiceCollection, Dispose, DiError};
use std::sync::Arc;
struct FileHandle;
impl Dispose for FileHandle {
fn dispose(&self) {
println!("Closing file");
}
}
let mut services = ServiceCollection::new();
services.add_scoped_factory::<FileHandle, _>(|_| FileHandle);
let provider = services.build();
let scope = provider.create_scope();
let result = scope.using_sync(|resolver| {
let file = resolver.get_disposable::<FileHandle>()?;
// Use the file...
Ok::<String, DiError>("File processed".to_string())
})?;
// FileHandle is automatically disposed here
assert_eq!(result, "File processed");Sourcepub fn create_child(&self) -> Self
pub fn create_child(&self) -> Self
Creates a child scope with fresh scoped state.
Used by labeled scopes for hierarchical scope management in workflow engines. The child scope inherits the same root ServiceProvider but has independent scoped storage.