Padrões de Projeto em Rust: Nível Avançado com Exemplos
\n\nPadrões de projeto são soluções comprovadas para problemas típicos que ajudam a criar uma arquitetura flexível, sustentável e escalável. Em Rust, com seu sistema de propriedade, empréstimo e traits, muitos padrões clássicos adquirem uma implementação única. Neste artigo, analisaremos padrões de projeto avançados que são especialmente eficazes no ecossistema Rust.
\n\n1. Builder Avançado (Construtor)
\n\nO padrão Builder clássico em Rust é frequentemente implementado através de uma cadeia de métodos que retornam &mut Self. No entanto, em nível avançado, usamos um Builder tipado com tipos de estado para garantir a correção em tempo de compilação.
Problema: controle de campos obrigatórios
\nUm Builder comum não garante que o usuário preencheu todos os campos obrigatórios. Podemos esquecer de chamar .build() ou não definir um parâmetro criticamente importante.
Solução: Builder com tipos fantasma
\nuse std::marker::PhantomData;\n\n// Tipos de estado para os campos\nstruct UsernameNotSet;\nstruct UsernameSet;\nstruct PasswordNotSet;\nstruct PasswordSet;\n\nstruct UserBuilder<U, P> {\n username: Option<String>,\n password: Option<String>,\n _username: PhantomData<U>,\n _password: PhantomData<P>,\n}\n\nimpl UserBuilder<UsernameNotSet, PasswordNotSet> {\n pub fn new() -> Self {\n Self {\n username: None,\n password: None,\n _username: PhantomData,\n _password: PhantomData,\n }\n }\n}\n\nimpl<P> UserBuilder<UsernameNotSet, P> {\n pub fn username(mut self, name: &str) -> UserBuilder<UsernameSet, P> {\n self.username = Some(name.to_string());\n UserBuilder {\n username: self.username,\n password: self.password,\n _username: PhantomData,\n _password: PhantomData,\n }\n }\n}\n\nimpl<U> UserBuilder<U, PasswordNotSet> {\n pub fn password(mut self, pwd: &str) -> UserBuilder<U, PasswordSet> {\n self.password = Some(pwd.to_string());\n UserBuilder {\n username: self.username,\n password: self.password,\n _username: PhantomData,\n _password: PhantomData,\n }\n }\n}\n\nimpl UserBuilder<UsernameSet, PasswordSet> {\n pub fn build(self) -> User {\n User {\n username: self.username.expect("Username must be set"),\n password: self.password.expect("Password must be set"),\n }\n }\n}\n\npub struct User {\n pub username: String,\n pub password: String,\n}\n\nfn main() {\n // Agora o compilador não permitirá chamar build() sem definir ambos os campos\n let user = UserBuilder::new()\n .username("alice")\n .password("secret123")\n .build();\n println!("User: {}", user.username);\n}\n\nVantagem: o erro "campo esquecido" é detectado em tempo de compilação, não em tempo de execução.
\n\n2. Padrão State (Estado) através de Traits
\n\nO padrão State permite que um objeto mude seu comportamento ao alterar seu estado interno. Em Rust, isso é elegantemente implementado através de enumerações (enum) e traits.
\n\nImplementação com enum
\nenum ConnectionState {\n Disconnected,\n Connecting,\n Connected,\n}\n\nstruct Connection {\n state: ConnectionState,\n}\n\nimpl Connection {\n pub fn new() -> Self {\n Self { state: ConnectionState::Disconnected }\n }\n\n pub fn connect(&mut self) {\n match self.state {\n ConnectionState::Disconnected => {\n println!("Iniciando conexão...");\n self.state = ConnectionState::Connecting;\n }\n ConnectionState::Connecting => {\n printl