Dependency Injection
Dependency Injection
Rusta provides a DI container for managing service dependencies.
Basic Injection
Mark structs as injectable and declare dependencies:
use rusta::prelude::*;use std::sync::Arc;
#[injectable]pub struct UserService { #[inject] repo: Arc<dyn UserRepository>,}
#[injectable]pub struct UserController { #[inject] svc: Arc<UserService>,}Registration
Register services in the container:
let mut container = Container::new();
// Register repositorycontainer.register(Arc::new(MongoUserRepository::new()));
// Register service (dependencies resolved automatically)container.register(UserService::construct(&container));
// Register controllercontainer.register(UserController::construct(&container));Optional Injection
Make dependencies optional - field becomes None if not registered:
#[injectable]pub struct EmailService { #[inject(optional)] smtp_client: Option<Arc<dyn SmtpClient>>,}
impl EmailService { pub fn send(&self, email: Email) -> Result<(), Error> { match &self.smtp_client { Some(client) => client.send(email).await, None => Err(Error::NotConfigured), } }}Named Bindings
Register multiple implementations of the same trait:
// Define two cache implementationscontainer.register_named(Arc::new(RedisCache::new()), "redis");container.register_named(Arc::new(MemoryCache::new()), "memory");
// Inject by name#[injectable]pub struct CacheService { #[inject(name = "redis")] primary: Arc<dyn Cache>, #[inject(name = "memory")] fallback: Arc<dyn Cache>,}Resolution in Handlers
Use the Inject extractor to resolve dependencies in handlers:
#[get("/users")]pub async fn list_users( Inject(svc): Inject<Arc<UserService>>,) -> Response { Http::json(svc.list().await)}Binding Verification
Verify all required bindings at startup:
let errors = container.verify();if !errors.is_empty() { panic!("Missing bindings: {:?}", errors);}This catches missing dependencies before the server starts.
Trait Objects
Use trait objects for abstraction:
#[async_trait]pub trait UserRepository: Send + Sync { async fn find(&self, id: &str) -> Option<User>; async fn save(&self, user: User) -> Result<User, Error>;}
#[injectable]pub struct UserService { #[inject] repo: Arc<dyn UserRepository>,}Container Reference
For middleware that needs access to the container:
async fn auth_middleware( State(container): State<ContainerRef>, request: Request, next: Next,) -> Response { let auth_svc: Arc<AuthService> = container.resolve(); // ...}