Introdução: Por que Rust é tão rigoroso e como o code review ajuda a não enlouquecer
\nVocê escreveu um código em Rust. Ele compilou — viva! Mas eis o problema: o desenvolvedor sênior, ao abrir seu Pull Request, suspira e começa a escrever comentários. Familiar? Rust é uma linguagem com uma filosofia única: ele confia em você, mas verifica tudo. No entanto, o compilador não vê o que o olho experiente vê. Ele não dirá: "Ei, isso pode ser feito de forma mais elegante" ou "Seu código está vazando — não memória, mas desempenho".
\nNeste artigo, faremos uma revisão de código em Rust e analisaremos observações típicas de seniores. Você aprenderá como transformar "código apenas funcional" em algo idiomático e confiável. Vamos lá!
\n\n1. Ignorar o sistema de tipos: "Vou colocar tudo em String"
\nO erro mais comum de iniciantes é usar String ou tipos genéricos onde tipos específicos são necessários. O sênior dirá: "Seu tipo deve refletir a lógica de negócios, não ser apenas uma string".
Problema: perda de semântica
\nImagine que você está escrevendo uma função para trabalhar com endereços de e-mail:
\nfn send_email(to: String, subject: String, body: String) {\n // ...\n}\nO que há de errado aqui? Você pode confundir to e subject — o compilador não perceberá. O sênior sugerirá usar o padrão newtype:
struct Email(String);\nstruct Subject(String);\nstruct Body(String);\n\nfn send_email(to: Email, subject: Subject, body: Body) {\n // ...\n}\nParece excessivo? Em um code review Rust, essa abordagem é o padrão ouro. Ela torna o código autodocumentado e elimina toda uma classe de erros.
\n\n2. Abuso de clone(): copiar não significa possuir
\nDesenvolvedores juniores costumam usar .clone() em todos os lugares onde o compilador reclama de empréstimos. O sênior dirá: "Você está apenas desistindo diante do borrow checker. Pense diferente".
Exemplo: clone desnecessário
\nfn process(data: Vec<i32>) {\n let first = data[0].clone();\n let second = data[1].clone();\n // ...\n}\ni32 implementa a trait Copy — clone() aqui é totalmente desnecessário. Mas o problema é mais profundo: se você clona estruturas grandes (como Vec ou String), terá um impacto no desempenho.
A abordagem correta é trabalhar com referências:
\nfn process(data: &[i32]) {\n let first = &data[0];\n let second = &data[1];\n // ...\n}\nEm uma revisão de código em Rust, você deve explicar por que está clonando. Se a resposta for "para o compilador não reclamar" — prepare-se para um comentário arrasador.
\n\n3. Tratamento de erros ruim: .unwrap() não é a solução final
\nSeniores odeiam unwrap() e expect() em código de produção. Por quê? Porque transformam um erro potencialmente recuperável em pânico. E pânico em Rust é uma terminação abrupta.
O que há de errado com este código?
\nlet file_content = std::fs::read_to_string("config.txt").unwrap();\nSe o arquivo não existir — o programa quebra. O sênior sugerirá:
\nlet file_content = std::fs::read_to_string("config.txt")\n .map_err(|e| format!("Falha ao ler config: {}", e))?;\nUse o operador ? e tipos de erro próprios. Essa é a base do Rust idiomático. Em um code review Rust, esta é uma das primeiras observações.
4. Desempenho esquecido: alocações e iteradores
\nRust é uma linguagem de sistema. O sênior olhará não apenas para a segurança, mas também para a velocidade. Um dos problemas típicos é criar coleções desnecessárias.
\nExemplo: colecionar sem necessidade
\nlet numbers: Vec<i32> = (0..100).collect();\nlet doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();