Contêineres de Injeção de Dependência em Rust: visão geral e implementação com o exemplo «dicy»

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Contêiner de Injeção de Dependência para Rust: visão geral da biblioteca dicy



No mundo da programação de sistemas, o Rust é famoso por seu desempenho e segurança de memória. No entanto, ao construir aplicações de grande porte, os desenvolvedores frequentemente enfrentam o problema de gerenciar dependências. A ligação manual de componentes leva a "código rígido", dificulta os testes e reduz a flexibilidade. A solução é um contêiner de Injeção de Dependência (DI), que automatiza a criação e injeção de dependências. Neste artigo, veremos a biblioteca dicy — um dos contêineres DI mais maduros para Rust.



O que é Injeção de Dependência e por que é necessária no Rust?



Injeção de Dependência é um padrão no qual um objeto recebe suas dependências de fora, em vez de criá-las ele mesmo. No Rust, isso é especialmente relevante devido ao sistema de tipos estrito e à propriedade (ownership). O contêiner DI assume:

  • Criação de instâncias de serviços (singleton, transient, scoped).
  • Resolução do grafo de dependências (injeção recursiva).
  • Gerenciamento do tempo de vida dos objetos.
  • Possibilidade de substituir implementações durante testes (objetos mock).


Biblioteca dicy: visão geral dos recursos



dicy é um contêiner DI leve, porém poderoso, escrito em Rust puro sem código unsafe. Ele suporta:

  • Resolução automática de dependências através de construtores ou fábricas.
  • Vários modos de tempo de vida: Singleton, Transient, Scoped (para actix-web, axum e outros frameworks).
  • Fábricas assíncronas — criação de serviços usando async/await.
  • Injeção via traits — vinculação de um trait a uma implementação específica.
  • Contêineres aninhados (para isolamento de módulos).


Instalação e configuração básica



Adicione dicy ao Cargo.toml:

[dependencies]dicy = "0.6"


Exemplo 1: Injeção simples via construtor



Vamos criar dois serviços: Config (singleton) e 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 } }}

// Define o módulo com o 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);

// Obtém Database com Config injetado automaticamente let db: Database = container.resolve().unwrap(); println!("Conexão ao BD: {}", db.config.url);}


Exemplo 2: Fábricas assíncronas e dependências Scoped



Para aplicações web, é frequentemente necessário criar serviços com escopo de requisição HTTP (scoped). O dicy suporta isso:

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