Expand description
LRDI — 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
LRDI 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 globallykeyed_scoped(key, factory)— shared within scopekeyed_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]collectsregister!()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()
§Crate layout
| Module | Description |
|---|---|
ServiceCollection | Register services with a builder API |
ServiceProvider | The root DI container |
Scope / ServiceScope | Scoped container (one per scope) |
IServiceResolver | Resolution trait (implemented by ServiceProvider & Scope) |
ServiceProviderWrapper | Child-first layered container |
ServiceLifetime | Enum: Singleton, Scoped, Transient |
IProvider | Unified trait for resolution + named registry |
RdiError | Error 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 scanningrust_dix::register!(...)— declare services inside#[rust_dix::module]
Re-exports§
pub use collection::ServiceCollection;pub use provider::ServiceProvider;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
- provider
- registration
- scope
- wrapper