Padrões de Projeto em Rust: quando e por que aplicá-los

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

Padrões de Projeto em Rust: não apenas moda, mas uma necessidade



Quando falamos de Rust, muitos imaginam um sistema de tipos, borrow checker e ausência de coletor de lixo. Mas por trás dessa fachada de rigor, esconde-se uma ferramenta poderosa para projetar arquitetura. Os padrões de projeto (design patterns) em Rust não são apenas uma homenagem às tradições do Java ou C++. São uma forma de fazer o compilador trabalhar a seu favor, e não contra você.



Neste artigo, vamos explorar por que os padrões clássicos do GoF (Gang of Four) em Rust são diferentes, quais deles são realmente úteis e quais são antipadrões. E o mais importante: veremos código real que pode ser aplicado imediatamente no seu projeto.



Por que o Rust muda as regras do jogo?



Os padrões de projeto clássicos geralmente resolvem problemas relacionados ao gerenciamento de memória, herança e polimorfismo. Rust oferece uma abordagem completamente diferente:



  • Propriedade e empréstimo — em vez de coleta de lixo, você gerencia a memória explicitamente. Padrões que em outras linguagens resolvem o problema de "quem é o dono do objeto" tornam-se triviais ou desnecessários em Rust.
  • Traits — substituem a herança. Rust não suporta herança OOP clássica, mas as traits oferecem flexibilidade sem os problemas da herança em diamante.
  • Enums e correspondência de padrões — uma ferramenta poderosa que substitui muitos padrões relacionados a estado e ramificação.
  • Estilo funcional — iteradores, closures e combinadores permitem escrever código conciso sem padrões explícitos.


Portanto, muitos padrões em Rust não se parecem com "classe e interface", mas sim com trait + struct + enum. Vamos ver exemplos concretos.



Padrão Builder: construindo objetos complexos



O padrão Builder é um dos mais populares em Rust. Ele é ideal para criar objetos com muitos parâmetros, especialmente quando alguns deles são opcionais.



Problema: construtor com 10 parâmetros



Imagine que você precisa criar uma configuração de cliente HTTP. Na abordagem tradicional, você escreveria:



let client = HttpClient::new(    "https://api.example.com",    Duration::from_secs(30),    5,    true,    None,    vec!["Accept: application/json".to_string()],    ...);


Isso é ilegível e fácil de errar a ordem dos argumentos. O Builder resolve esse problema.



Implementação em Rust



Em Rust, o padrão Builder é implementado através de uma struct separada com métodos setters que retornam self:



#[derive(Debug)]pub struct HttpClient {    base_url: String,    timeout: Duration,    max_retries: u32,    verify_ssl: bool,    headers: Vec<String>,}

pub struct HttpClientBuilder { base_url: String, timeout: Duration, max_retries: u32, verify_ssl: bool, headers: Vec<String>,}

impl HttpClientBuilder { pub fn new(base_url: &str) -> Self { HttpClientBuilder { base_url: base_url.to_string(), timeout: Duration::from_secs(30), max_retries: 3, verify_ssl: true, headers: Vec::new(), } }

pub fn timeout(mut self, duration: Duration) -> Self { self.timeout = duration; self }

pub fn max_retries(mut self, n: u32) -> Self { self.max_retries = n; self }

pub fn no_ssl(mut self) -> Self { self.verify_ssl = false; self }

pub fn add_header(mut self, header: &str) -> Self { self.headers.push(header.to_string()); self }

pub fn build(self) -> Result<HttpClient, String> { if self.base_url.is_empty() { return Err("base_url cannot be empty".to_string()); } Ok(HttpClient { base_url: self.base_url,

Blogs

Book Recommendations