Dependency Injection Container in Rust: Überblick und Implementierung am Beispiel von «dicy»

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Dependency Injection Container für Rust: Überblick über die Bibliothek dicy



In der Welt der Systemprogrammierung ist Rust für seine Leistung und Speichersicherheit bekannt. Bei der Entwicklung großer Anwendungen stehen Entwickler jedoch oft vor dem Problem der Abhängigkeitsverwaltung. Manuelles Verknüpfen von Komponenten führt zu „hartcodiertem“ Code, erschwert das Testen und verringert die Flexibilität. Die Lösung ist ein Dependency Injection (DI) Container, der die Erstellung und Injektion von Abhängigkeiten automatisiert. In diesem Artikel betrachten wir die Bibliothek dicy – einen der ausgereiftesten DI Container für Rust.



Was ist Dependency Injection und warum wird es in Rust benötigt?



Dependency Injection ist ein Entwurfsmuster, bei dem ein Objekt seine Abhängigkeiten von außen erhält, anstatt sie selbst zu erstellen. In Rust ist dies aufgrund des strengen Typsystems und des Besitzmodells (ownership) besonders relevant. Ein DI Container übernimmt:

  • Die Erstellung von Service-Instanzen (Singleton, Transient, Scoped).
  • Die Auflösung des Abhängigkeitsgraphen (rekursive Injektion).
  • Die Verwaltung der Lebensdauer von Objekten.
  • Die Möglichkeit, Implementierungen beim Testen auszutauschen (Mock-Objekte).


Die Bibliothek dicy: Überblick über die Funktionen



dicy ist ein leichtgewichtiger, aber leistungsstarker DI Container, der in reinem Rust ohne unsicheren Code geschrieben ist. Er unterstützt:

  • Automatische Auflösung von Abhängigkeiten über Konstruktoren oder Fabriken.
  • Mehrere Lebensdauermodi: Singleton, Transient, Scoped (für actix-web, axum und andere Frameworks).
  • Asynchrone Fabriken – Erstellung von Diensten mit async/await.
  • Injektion über Traits – Bindung eines Traits an eine konkrete Implementierung.
  • Verschachtelte Container (zur Isolierung von Modulen).


Installation und grundlegende Einrichtung



Fügen Sie dicy zu Ihrer Cargo.toml hinzu:

[dependencies]dicy = "0.6"


Beispiel 1: Einfache Injektion über den Konstruktor



Wir erstellen zwei Dienste: Config (Singleton) und Database (Transient), der von Config abhängt.

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 } }}

// Modul mit Registrierung definierenstruct 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);

// Database mit automatisch injiziertem Config abrufen let db: Database = container.resolve().unwrap(); println!("Verbindung zur DB: {}", db.config.url);}


Beispiel 2: Asynchrone Fabriken und Scoped-Abhängigkeiten



Für Webanwendungen müssen oft Dienste mit dem Gültigkeitsbereich einer HTTP-Anfrage (scoped) erstellt werden. dicy unterstützt dies:

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!("User #{}", self.ctx.user_id) }}

struct WebModule;

impl Module for WebModule { fn configure(container: &mut Container) { container

Empfehlungen