Padrões de Projeto em Rust: da Teoria à Prática
Padrões de projeto são soluções comprovadas para problemas recorrentes no desenvolvimento de software. Em Rust, graças ao seu sistema único de ownership (propriedade) e à ausência de POO clássica, muitos padrões clássicos ganham uma nova roupagem. Neste artigo, abordaremos quatro padrões-chave: Singleton, Factory, Builder e Strategy, adaptados para o Rust idiomático.
1. Singleton (Instância Única) em Rust
Rust não possui variáveis estáticas globais no sentido tradicional. No entanto, o Singleton pode ser implementado através de lazy_static ou OnceCell. Vejamos um exemplo com std::sync::OnceLock (disponível desde o Rust 1.70).
Implementação com OnceLock
use std::sync::OnceLock;
static CONFIG: OnceLock<Config> = OnceLock::new();
#[derive(Debug)]struct Config { db_url: String, port: u16,}
fn get_config() -> &'static Config { CONFIG.get_or_init(|| { Config { db_url: String::from("postgres://localhost"), port: 5432, } })}
fn main() { let cfg = get_config(); println!("{:?}", cfg);}Ponto-chave: OnceLock garante inicialização única e segurança em threads. Esta é a forma idiomática para Rust.
2. Factory (Fábrica) através de Traits
O padrão Factory em Rust é implementado de forma excelente com traits. Não precisamos de classes abstratas — basta definir uma interface através de um trait.
Exemplo: criação de meios de transporte
trait Transport { fn deliver(&self);}
struct Truck;impl Transport for Truck { fn deliver(&self) { println!("Entrega por caminhão em terra"); }}
struct Ship;impl Transport for Ship { fn deliver(&self) { println!("Entrega por navio no mar"); }}
struct LogisticsFactory;
impl LogisticsFactory { fn create_transport(route_type: &str) -> Box<dyn Transport> { match route_type { "land" => Box::new(Truck), "sea" => Box::new(Ship), _ => panic!("Tipo de rota desconhecido"), } }}
fn main() { let transport = LogisticsFactory::create_transport("sea"); transport.deliver();}Vantagem: Rust não requer herança — o polimorfismo é alcançado através de traits e Box<dyn Trait>.
3. Builder (Construtor) com encadeamento tipado
O Builder em Rust é amplamente utilizado, especialmente para estruturas com muitos parâmetros opcionais. Vamos implementá-lo com um encadeamento tipado de métodos que retornam &mut Self.
Exemplo: criação de uma requisição HTTP
#[derive(Debug, Default)]struct HttpRequest { url: String, method: String, headers: Vec<(String, String)>, body: Option<String>,}
struct HttpRequestBuilder { request: HttpRequest,}
impl HttpRequestBuilder { fn new(url: &str) -> Self { HttpRequestBuilder { request: HttpRequest { url: url.to_string(), method: "GET".to_string(), ..Default::default() }, } }
fn method(&mut self, method: &str) -> &mut Self { self.request.method = method.to_string(); self }
fn header(&mut self, key: &str, value: &str) -> &mut Self { self.request.headers.push((key.to_string(), value.to_string())); self }
fn body(&mut self, body: &str) -> &mut Self { self.request.body = Some(body.to_string()); self }
fn build(&self) -> HttpRequest { // Aqui pode-se adicionar validação HttpRequest { ..self.request.clone() } }}
fn ma