Contenedores de Inyección de Dependencias en Rust: descripción general e implementación con el ejemplo de «dicy»

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Contenedor de Inyección de Dependencias para Rust: descripción general de la biblioteca dicy



En el mundo de la programación de sistemas, Rust es famoso por su rendimiento y seguridad de memoria. Sin embargo, al construir aplicaciones grandes, los desarrolladores a menudo se enfrentan al problema de gestionar las dependencias. La vinculación manual de componentes conduce a «código rígido», dificulta las pruebas y reduce la flexibilidad. La solución es un contenedor de Inyección de Dependencias (DI), que automatiza la creación e inyección de dependencias. En este artículo, analizaremos la biblioteca dicy, uno de los contenedores DI más maduros para Rust.



¿Qué es la Inyección de Dependencias y por qué es necesaria en Rust?



La Inyección de Dependencias es un patrón en el que un objeto recibe sus dependencias desde el exterior, en lugar de crearlas él mismo. En Rust, esto es especialmente relevante debido al estricto sistema de tipos y propiedad (ownership). Un contenedor DI se encarga de:

  • Creación de instancias de servicios (singleton, transient, scoped).
  • Resolución del grafo de dependencias (inyección recursiva).
  • Gestión del ciclo de vida de los objetos.
  • Posibilidad de sustituir implementaciones durante las pruebas (objetos mock).


Biblioteca dicy: descripción general de capacidades



dicy es un contenedor DI ligero pero potente, escrito en Rust puro sin código unsafe. Soporta:

  • Resolución automática de dependencias a través de constructores o fábricas.
  • Varios modos de ciclo de vida: Singleton, Transient, Scoped (para actix-web, axum y otros frameworks).
  • Fábricas asíncronas — creación de servicios usando async/await.
  • Inyección a través de traits — vinculación de un trait a una implementación concreta.
  • Contenedores anidados (para aislamiento de módulos).


Instalación y configuración básica



Añada dicy a su Cargo.toml:

[dependencies]dicy = "0.6"


Ejemplo 1: Inyección simple por constructor



Creemos dos servicios: Config (singleton) y Database (transient), que depende 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 } }}

// Definimos el módulo con el registrostruct 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);

// Obtenemos Database con Config inyectado automáticamente let db: Database = container.resolve().unwrap(); println!("Conexión a BD: {}", db.config.url);}


Ejemplo 2: Fábricas asíncronas y dependencias Scoped



Para aplicaciones web, a menudo es necesario crear servicios con ámbito de solicitud HTTP (scoped). dicy lo soporta:

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

Recommendations