Skip to main content

Crate rust_dix

Crate rust_dix 

Source
Expand description

rust-dix — Rust Dependency Injection Framework

A dependency injection container for Rust inspired by Microsoft.Extensions.DependencyInjection (MEDI).

§Quick start

use rust_dix::*;
use std::sync::Arc;

// Trait-based interface (recommended)
trait ILogger: Send + Sync {
    fn log(&self, msg: &str);
}

struct ConsoleLogger;
impl ILogger for ConsoleLogger {
    fn log(&self, msg: &str) { println!("[LOG] {}", msg); }
}

// Register as trait interface
let provider = ServiceCollection::new()
    .singleton::<dyn ILogger>(|_| Arc::new(ConsoleLogger))
    .build()
    .unwrap();

// Resolve as trait
let logger: Arc<dyn ILogger> = provider.get().unwrap();
logger.log("Hello");

§Keyed services (strategy pattern)

use rust_dix::*;
use std::sync::Arc;

trait Strategy: Send + Sync { fn execute(&self) -> &'static str; }

struct FastStrategy;
impl Strategy for FastStrategy { fn execute(&self) -> &'static str { "fast" } }

struct SafeStrategy;
impl Strategy for SafeStrategy { fn execute(&self) -> &'static str { "safe" } }

let provider = ServiceCollection::new()
    .keyed_singleton::<dyn Strategy>("fast", |_| Arc::new(FastStrategy))
    .keyed_singleton::<dyn Strategy>("safe", |_| Arc::new(SafeStrategy))
    .build()
    .unwrap();

let fast: Arc<dyn Strategy> = provider.get_keyed("fast").unwrap();
assert_eq!(fast.execute(), "fast");

§Build-time validation

rust-dix validates dependencies during build() to catch errors early:

// Circular dependency detection — factories resolve deps via `get_any`
// (the `get::<T>()` method requires `Self: Sized` and cannot be called
// on `&dyn IServiceResolver`).
let result = ServiceCollection::new()
    .singleton(|r| {
        let _b = r.get_any(std::any::type_name::<B>());  // A depends on B
        Arc::new(A)
    })
    .singleton(|r| {
        let _a = r.get_any(std::any::type_name::<A>());  // B depends on A → CIRCULAR!
        Arc::new(B)
    })
    .build();

assert!(result.is_err());
if let Err(RdiError::CircularDependency(cycle)) = result {
    println!("Circular dependency detected: {}", cycle);
}

§Async support

For services that require async initialization (e.g., database connections, remote config loading), use build_async() and the async_* registration methods. build_async() returns Arc<ServiceProvider> so that provider_arc() is available for async resolution:

ⓘ
use rust_dix::*;
use std::sync::Arc;

struct DbPool { conn: String }

async fn connect_to_db() -> DbPool {
    // async connection setup...
    DbPool { conn: "connected".into() }
}

let provider = ServiceCollection::new()
    .async_singleton(|_| Box::pin(async {
        Arc::new(connect_to_db().await)
    }))
    .build_async()
    .await
    .unwrap();

let pool: Arc<DbPool> = provider.get_async().await.unwrap();

§Features

  • Three lifetimes: Singleton, Scoped, Transient
  • Keyed services: multiple implementations of the same trait by key
    • keyed_singleton(key, factory) — shared globally
    • keyed_scoped(key, factory) — shared within scope
    • keyed_transient(key, factory) — new instance each time
  • Constructor injection: #[derive(Inject)] on structs
  • Attribute-based auto-registration: #[rust_dix::inject] on structs or trait impls
  • Compile-time module scanning: #[rust_dix::module] collects register!() declarations
  • Cross-DLL support: named service registry for cdylib plugins
  • Layered containers: child-first resolution via ServiceProviderWrapper
  • Build-time validation: circular dependency detection during build()
  • Scope validation: scoped services cannot be resolved from the root provider by default (MEDI ValidateScopes parity; opt out with ServiceProviderOptions::lenient)
  • Deterministic lifecycle: eager singletons are created in registration order; ServiceProvider::dispose / Scope::dispose release cached instances in reverse creation order
  • MEDI registration semantics: the last registration wins for a single service; get_all returns unkeyed implementations in registration order (get_all_keyed for keyed ones)

§Crate layout

ModuleDescription
ServiceCollectionRegister services with a builder API
ServiceProviderThe root DI container
Scope / ServiceScopeScoped container (one per scope)
IServiceResolverResolution trait (implemented by ServiceProvider & Scope)
ServiceProviderWrapperChild-first layered container
ServiceLifetimeEnum: Singleton, Scoped, Transient
IProviderUnified trait for resolution + named registry
RdiErrorError types (includes CircularDependency)

§Proc-macros

  • #[derive(Inject)] — auto-generates constructor injection code
  • #[rust_dix::inject] — auto-registration on structs or trait impls
  • #[rust_dix::module] — compile-time module scanning
  • rust_dix::register!(...) — declare services inside #[rust_dix::module]

Re-exports§

pub use collection::ServiceCollection;
pub use provider::ServiceProvider;
pub use key::KeyedService;
pub use key::ServiceKey;
pub use provider::AsyncFactoryContext;
pub use provider::ServiceProviderOptions;
pub use entry::ServiceDescriptor;
pub use entry::ServiceLifetime;
pub use entry::IServiceResolver;
pub use scope::Scope;
pub use scope::Scope as ServiceScope;
pub use scope::ScopeFactory;
pub use entry::ServiceEntry;
pub use entry::ServiceFactory;
pub use wrapper::ServiceProviderWrapper;
pub use bridge::IProvider;
pub use error::RdiError;
pub use registration::ServiceRegistration;

Modules§

bridge
Unified provider trait for service resolution and named registry access.
collection
entry
error
key
Service keys for keyed registrations.
provider
registration
scope
wrapper

Macros§

register

Attribute Macros§

inject
module

Derive Macros§

Inject