Scope

Struct Scope 

Source
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

Source

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 resources
Source

pub async fn using<F, Fut, R, E>(&self, f: F) -> Result<R, E>
where F: FnOnce(ScopedResolver) -> Fut, Fut: Future<Output = Result<R, E>>, E: From<DiError>,

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):

  1. Async disposers run first (in reverse order)
  2. 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");
Source

pub fn using_sync<F, R, E>(&self, f: F) -> Result<R, E>
where F: FnOnce(ScopedResolver) -> Result<R, E>, E: From<DiError>,

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");
Source

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.

Trait Implementations§

Source§

impl Clone for Scope

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Drop for Scope

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl Resolver for Scope

Source§

fn register_disposer<T>(&self, service: Arc<T>)
where T: Dispose + 'static,

Registers a service for synchronous disposal. Read more
Source§

fn register_async_disposer<T>(&self, service: Arc<T>)
where T: AsyncDispose + 'static,

Registers a service for asynchronous disposal. Read more
Source§

fn get<T: 'static + Send + Sync>(&self) -> DiResult<Arc<T>>

Resolves a concrete service type. Read more
Source§

fn get_trait<T: ?Sized + 'static + Send + Sync>(&self) -> DiResult<Arc<T>>
where Arc<T>: 'static,

Resolves a single trait implementation. Read more
Source§

fn get_all_trait<T: ?Sized + 'static + Send + Sync>( &self, ) -> DiResult<Vec<Arc<T>>>
where Arc<T>: 'static,

Resolves all registered implementations of a trait. Read more
Source§

fn get_required<T: 'static + Send + Sync>(&self) -> Arc<T>

Resolves a concrete service type, panicking on failure. Read more
Source§

fn get_required_trait<T: ?Sized + 'static + Send + Sync>(&self) -> Arc<T>
where Arc<T>: 'static,

Resolves a trait implementation, panicking on failure. Read more
Source§

fn get_named<T: 'static + Send + Sync>( &self, name: &'static str, ) -> DiResult<Arc<T>>

Resolves a named concrete service type. Read more
Source§

fn get_named_required<T: 'static + Send + Sync>( &self, name: &'static str, ) -> Arc<T>

Resolves a named concrete service type, panicking on failure.
Source§

fn get_named_trait<T: ?Sized + 'static + Send + Sync>( &self, name: &'static str, ) -> DiResult<Arc<T>>
where Arc<T>: 'static,

Resolves a named trait implementation.
Source§

fn get_named_trait_required<T: ?Sized + 'static + Send + Sync>( &self, name: &'static str, ) -> Arc<T>
where Arc<T>: 'static,

Resolves a named trait implementation, panicking on failure.
Source§

impl ResolverCore for Scope

Source§

fn resolve_any(&self, key: &Key) -> DiResult<Arc<dyn Any + Send + Sync>>

Resolves a single service using thread-local stack for circular dependency detection. Read more
Source§

fn resolve_many(&self, key: &Key) -> DiResult<Vec<Arc<dyn Any + Send + Sync>>>

Resolves all multi-bound services for a trait using circular dependency detection. Read more
Source§

fn push_sync_disposer(&self, f: Box<dyn FnOnce() + Send>)

Registers a synchronous disposal hook. Read more
Source§

fn push_async_disposer( &self, f: Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send>, )

Registers an asynchronous disposal hook. Read more
Source§

fn resolve_any_internal( &self, key: &Key, ) -> DiResult<Arc<dyn Any + Send + Sync>>

Legacy internal resolve method for compatibility. Read more
Source§

fn resolve_many_internal( &self, key: &Key, ) -> DiResult<Vec<Arc<dyn Any + Send + Sync>>>

Legacy internal resolve many method for compatibility. Read more
Source§

impl ScopeCancellationExt for Scope

Source§

fn with_cancellation_from_parent(&self) -> Self

Creates a child scope with a cancellation token derived from the parent scope. Read more

Auto Trait Implementations§

§

impl !Freeze for Scope

§

impl !RefUnwindSafe for Scope

§

impl Send for Scope

§

impl Sync for Scope

§

impl Unpin for Scope

§

impl !UnwindSafe for Scope

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.