Injeção de Dependência em Rust: guia com exemplos de código

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

Introdução à Injeção de Dependência em Rust



Injeção de Dependência (DI) é um padrão de projeto no qual um objeto recebe suas dependências externamente, em vez de criá-las por conta própria. Em Rust, onde segurança e desempenho são prioridades, a DI ajuda a criar código flexível, testável e com baixo acoplamento. Neste artigo, vamos analisar como implementar a injeção de dependências em Rust, usando traits, genéricos e contêineres.



Diferente de linguagens com reflexão (Java, C#), Rust não fornece frameworks DI embutidos. No entanto, seu sistema de tipos e propriedade permite criar soluções elegantes sem perda de desempenho.



Abordagens principais para Injeção de Dependência



Em Rust, existem várias maneiras de implementar DI. Vamos considerar três abordagens principais.



1. Injeção via construtor (Constructor Injection)



A maneira mais simples e comum é passar as dependências no construtor da struct. Isso garante que o objeto esteja sempre em um estado válido.



// Definimos um trait para enviar notificaçõestrait Notifier {    fn send(&self, message: &str);}

// Implementação para Emailstruct EmailNotifier { smtp_server: String,}

impl Notifier for EmailNotifier { fn send(&self, message: &str) { println!("Enviando email via {}: {}", self.smtp_server, message); }}

// Implementação para SMSstruct SmsNotifier { api_key: String,}

impl Notifier for SmsNotifier { fn send(&self, message: &str) { println!("Enviando SMS com chave {}: {}", self.api_key, message); }}

// Serviço que usa Notifierstruct NotificationService { notifier: Box<dyn Notifier>,}

impl NotificationService { // Injeção de dependência via construtor fn new(notifier: Box<dyn Notifier>) -> Self { NotificationService { notifier } }

fn notify(&self, message: &str) { self.notifier.send(message); }}

fn main() { let email_notifier = Box::new(EmailNotifier { smtp_server: "smtp.example.com".to_string(), }); let service = NotificationService::new(email_notifier); service.notify("Hello, Rust!");}


2. Injeção via genéricos (Generic Injection)



O uso de genéricos permite evitar a despacho dinâmico e aumentar o desempenho por meio da monomorfização.



// Mesmo trait Notifier, mas o serviço agora é genéricostruct NotificationService<T: Notifier> {    notifier: T,}

impl<T: Notifier> NotificationService<T> { fn new(notifier: T) -> Self { NotificationService { notifier } }

fn notify(&self, message: &str) { self.notifier.send(message); }}

fn main() { let email_notifier = EmailNotifier { smtp_server: "smtp.example.com".to_string(), }; let service = NotificationService::new(email_notifier); service.notify("Hello from generic Rust!");}


3. Injeção via contêiner de dependências



Para projetos complexos, é conveniente usar um contêiner de dependências, que gerencia a criação e o tempo de vida dos objetos. Em Rust, é possível implementar um contêiner simples manualmente.



use std::collections::HashMap;

// Trait para fábricas de dependênciastrait Factory<T> { fn create(&self) -> T;}

// Contêiner de dependências simplesstruct Container { factories: HashMap<String, Box<dyn std::any::Any>>,}

impl Container { fn new() -> Self { Container { factories: HashMap::new(), } }

fn register<T: 'static>(&mut self, name: &str, factory: Box<dyn Factory<T>>) { self.factories.insert(name.to_string(), Box::new(factory)); }

fn resolve<T: 'static>(&self, name: &str) -> Option<T> { self.factories.get(name).and_then(|f|

Blogs

Book Recommendations