Por que code review em Rust não é apenas verificar chaves
Rust é uma linguagem que promete segurança de memória e alta performance. Mas mesmo o compilador mais rigoroso não protege contra erros lógicos, código não idiomático ou uso injustificado de unsafe. Code review em Rust é uma arte. Você não está apenas verificando se as chaves estão fechadas. Você verifica como o desenvolvedor contornou (ou não) as limitações do borrow checker, quão eficientemente usa iteradores e se não está tentando "burlar" o sistema com ponteiros brutos.
Neste artigo, falarei sobre quais aspectos prestar atenção ao revisar código em Rust. Sem teoria desnecessária — apenas dicas práticas, exemplos e uma checklist que você pode pendurar na parede (ou salvar nos favoritos). Vamos lá!
1. Ownership, borrowing e lifetimes: a principal fonte de dor
A primeira coisa a verificar é se o sistema de ownership está funcionando corretamente. Mesmo que o código compile, isso não significa que ele está correto. Problemas típicos:
- Clonagem excessiva. Quando poderia ser usada uma referência, o desenvolvedor copia os dados. Isso prejudica a performance.
- Borrowing mutável e imutável. Se no mesmo escopo houver
&mut Te&Tsimultaneamente — haverá erro. Mas às vezes é possível reordenar o código para evitar isso. - Lifetimes incorretos. Especialmente em estruturas complexas com múltiplas referências.
Exemplo: código ruim
fn process(data: &Vec<i32>) -> i32 { let mut sum = 0; for &x in data.iter() { sum += x; } // Tudo ok, mas por que passar &Vec, se pode &[i32]? sum}Código bom
fn process(data: &[i32]) -> i32 { data.iter().sum()}O que verificar na revisão: não está sendo passado &Vec, &String ou &HashMap onde um slice é suficiente? clone() está sendo usado desnecessariamente? As anotações de lifetime estão corretas?
2. Unsafe: zona vermelha
Blocos unsafe são como um alçapão aberto no Rust seguro. Eles devem ser justificados, mínimos e cuidadosamente documentados. No code review, preste atenção em:
- Por que unsafe está sendo usado? Muitas vezes é possível usar abstrações seguras.
- Corretude dos ponteiros brutos. Um ponteiro nulo está sendo desreferenciado? Uma referência está pendente após a liberação de memória?
- Invariantes. Código com
unsafedeve descrever explicitamente quais invariantes ele mantém.
Exemplo: unsafe perigoso
unsafe fn dangerous() { let ptr = 0x12345 as *mut i32; *ptr = 42; // E se não houver nada neste endereço?}Esse código não deve passar na revisão. Mesmo que "funcione" — isso não é um argumento.
Unsafe bom
unsafe fn init_buffer(buf: &mut [u8]) { // Temos certeza de que buf aponta para memória válida, // pois a parte chamadora garante isso. std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len());}O que verificar: há um comentário justificando cada unsafe? Existem testes que verificam casos limite? As garantias de segurança estão sendo violadas?
3. Closures e estilo funcional
Rust incentiva a abordagem funcional: iteradores, closures, map, filter, fold. Mas isso pode levar à complexidade excessiva. Na revisão, observe:
- Há abuso de closures? Se uma closure captura muitas variáveis, talvez valha a pena extrair a lógica para uma função separada.