Introdução: SOLID e Rust — uma combinação ideal
\nOs princípios SOLID são regras fundamentais de design orientado a objetos que ajudam a criar código flexível, sustentável e escalável. Embora Rust não seja uma linguagem puramente orientada a objetos (ela usa traits e structs em vez de classes), os princípios SOLID são totalmente aplicáveis a ela. Neste artigo, analisaremos cada um dos cinco princípios com exemplos de código em Rust, mostrando como eles ajudam a construir sistemas robustos.
\nAo longo do artigo, você verá como Rust, com seu sistema de ownership, traits e genéricos, torna a implementação do SOLID natural e poderosa. Começaremos com o Single Responsibility e terminaremos com o Dependency Inversion, fornecendo exemplos prontos para uso.
\n\n1. Single Responsibility Principle (SRP) — princípio da responsabilidade única
\nEssência: Cada módulo, classe ou função deve ter apenas um motivo para mudar. Em Rust, isso significa que uma struct deve ser responsável por uma única tarefa.
\n\nExemplo ruim (violação do SRP)
\nstruct Report {\n title: String,\n content: String,\n}\n\nimpl Report {\n fn generate(&self) -> String {\n format!("# {}\\n\\n{}", self.title, self.content)\n }\n\n fn save_to_file(&self, path: &str) -> std::io::Result<()> {\n std::fs::write(path, self.generate())?;\n Ok(())\n }\n\n fn send_email(&self, recipient: &str) {\n println!("Sending email to {} with report", recipient);\n }\n}\nAqui, a struct Report é responsável por gerar, salvar e enviar — três razões diferentes para mudar. Isso viola o SRP.
Exemplo bom (cumprimento do SRP)
\nstruct Report {\n title: String,\n content: String,\n}\n\nimpl Report {\n fn generate(&self) -> String {\n format!("# {}\\n\\n{}", self.title, self.content)\n }\n}\n\nstruct ReportSaver;\n\nimpl ReportSaver {\n fn save(&self, report: &Report, path: &str) -> std::io::Result<()> {\n std::fs::write(path, report.generate())?;\n Ok(())\n }\n}\n\nstruct EmailSender;\n\nimpl EmailSender {\n fn send(&self, recipient: &str, message: &str) {\n println!("Sending email to {}: {}", recipient, message);\n }\n}\nAgora cada struct é responsável por sua tarefa: Report pelos dados e geração, ReportSaver por salvar, EmailSender por enviar. Isso simplifica os testes e a manutenção.
2. Open-Closed Principle (OCP) — princípio aberto/fechado
\nEssência: Entidades de software devem estar abertas para extensão, mas fechadas para modificação. Em Rust, isso é alcançado através de traits e genéricos.
\n\nExemplo: sistema de filtragem
\ntrait Filter<T> {\n fn apply(&self, items: &[T]) -> Vec<T>;\n}\n\nstruct EvenFilter;\n\nimpl Filter<i32> for EvenFilter {\n fn apply(&self, items: &[i32]) -> Vec<i32> {\n items.iter().filter(|&x| x % 2 == 0).copied().collect()\n }\n}\n\nstruct GreaterThanFilter {\n threshold: i32,\n}\n\nimpl Filter<i32> for GreaterThanFilter {\n fn apply(&self, items: &[i32]) -> Vec<i32> {\n items.iter().filter(|&&x| x > self.threshold).copied().collect()\n }\n}\n\nfn process<T: Clone, F: Filter<T>>(items: &[T], filter: F) -> Vec<T> {\n filter.apply(items)\n}\n\nfn main() {\n let numbers = vec![1, 2, 3, 4, 5, 6];\n let even = process(&numbers, EvenFilter);\n let greater = process(&numbers, GreaterThanFilter { threshold: 3 });\n println!("Even: {:?}, Greater: {:?}", even, greater);\n}\nPodemos adicionar novos filtros sem modificar o código existente de process. Esta é a implementação do OCP através do polimorfismo de traits.
3. Liskov Substitution Principle (LSP) — princípio da substituição de Barbara Liskov
\nEssência:
Blogs
Book Recommendations
Leia também
OAuth 2.0 e autorização JWT em Rust: guia passo a passo para iniciantes
Aliases em tsconfig.json: como funcionam os caminhos no TypeScript
Erro TypeScript TS2304: cannot find name 'require' — como corrigir em 2025
Server-Sent Events em Rust: enviando dados em tempo real sem WebSocket
Funções Serverless em Kotlin: AWS Lambda — Guia Completo