Conteneur d'injection de dépendances pour Rust : aperçu de la bibliothèque dicy
Dans le monde de la programmation système, Rust est réputé pour ses performances et sa sécurité mémoire. Cependant, lors de la construction d'applications de grande envergure, les développeurs sont souvent confrontés au problème de la gestion des dépendances. Le couplage manuel des composants conduit à un « code rigide », complique les tests et réduit la flexibilité. La solution est un conteneur d'injection de dépendances (DI), qui automatise la création et l'injection des dépendances. Dans cet article, nous examinons la bibliothèque dicy — l'un des conteneurs DI les plus matures pour Rust.
Qu'est-ce que l'injection de dépendances et pourquoi en a-t-on besoin en Rust ?
L'injection de dépendances est un modèle dans lequel un objet reçoit ses dépendances de l'extérieur, plutôt que de les créer lui-même. En Rust, cela est particulièrement pertinent en raison du système de types strict et de la propriété (ownership). Le conteneur DI prend en charge :
- La création d'instances de services (singleton, transient, scoped).
- La résolution du graphe de dépendances (injection récursive).
- La gestion de la durée de vie des objets.
- La possibilité de substituer des implémentations lors des tests (objets mock).
La bibliothèque dicy : aperçu des fonctionnalités
dicy est un conteneur DI léger mais puissant, écrit en Rust pur sans code unsafe. Il prend en charge :
- La résolution automatique des dépendances via des constructeurs ou des fabriques.
- Plusieurs modes de durée de vie : Singleton, Transient, Scoped (pour actix-web, axum et autres frameworks).
- Les fabriques asynchrones — création de services avec async/await.
- L'injection via des traits — liaison d'un trait à une implémentation spécifique.
- Les conteneurs imbriqués (pour l'isolation des modules).
Installation et configuration de base
Ajoutez dicy dans Cargo.toml :
[dependencies]dicy = "0.6"Exemple 1 : Injection simple par constructeur
Créons deux services : Config (singleton) et Database (transient), qui dépend de Config.
use dicy::{Container, Module, Provide, Singleton, Transient};
#[derive(Clone)]struct Config { url: String,}
impl Config { fn new() -> Self { Config { url: "postgres://localhost".into() } }}
struct Database { config: Config,}
impl Database { fn new(config: Config) -> Self { Database { config } }}
// Définition du module avec enregistrementstruct AppModule;
impl Module for AppModule { fn configure(container: &mut Container) { container .register::<Config>(Singleton::new(Config::new)) .register::<Database>(Transient::new(Database::new)); }}
fn main() { let container = Container::new(); container.install(AppModule);
// Obtention de Database avec Config injecté automatiquement let db: Database = container.resolve().unwrap(); println!("Connexion à la BD : {}", db.config.url);}Exemple 2 : Fabriques asynchrones et dépendances scopées
Pour les applications web, il est souvent nécessaire de créer des services avec une portée de requête HTTP (scoped). dicy prend en charge cela :
use dicy::{Container, Module, Scoped, AsyncFactory, Provide};use async_trait::async_trait;
struct RequestContext { user_id: u64,}
#[async_trait]trait UserService { async fn get_name(&self) -> String;}
struct UserServiceImpl { ctx: RequestContext,}
#[async_trait]impl UserService for UserServiceImpl { async fn get_name(&self) -> String { format!("Utilisateur #{}", self.ctx.user_id) }}
struct WebModule;
impl Module for WebModule { fn configure(container: &mut Container) { container Aussi dans la bibliothèque
Clikt : Un framework CLI puissant pour Kotlin avec une syntaxe déclarative
Génération de PDF en JavaScript : bibliothèque pdf-lib
Traitement Markdown en Swift pour iOS et macOS : bibliothèques et exemples
Parsing HTML en Swift (iOS/macOS) : bibliothèques SwiftSoup et Fuzi