Introdução
Rust é uma poderosa linguagem de programação de sistemas que está ganhando popularidade devido à sua segurança e desempenho. No entanto, seu sistema de ownership (propriedade) e borrowing (empréstimo) frequentemente confunde iniciantes. Neste artigo, vamos analisar os principais erros de desenvolvedores Rust iniciantes para que você possa evitá-los e se familiarizar mais rapidamente com a linguagem. Abordaremos problemas típicos com borrowing, lifetimes (tempo de vida), strings e gerenciamento de memória.
1. Ignorar as regras de borrowing (Borrow Checker)
O erro mais comum é tentar ter simultaneamente uma referência mutável e uma imutável para os mesmos dados. Rust proíbe estritamente isso em tempo de compilação.
Problema: múltiplas referências
fn main() {
let mut data = vec![1, 2, 3];
let ref1 = &data; // referência imutável
let ref2 = &mut data; // referência mutável — ERRO!
println!("{} {}", ref1[0], ref2[0]);
}
O compilador emitirá um erro: cannot borrow `data` as mutable because it is also borrowed as immutable.
Solução: escopo correto
fn main() {
let mut data = vec![1, 2, 3];
{
let ref1 = &data; // referência imutável
println!("{}", ref1[0]);
} // ref1 sai de escopo
let ref2 = &mut data; // agora é possível
ref2[0] = 10;
println!("{}", ref2[0]);
}
Sempre monitore o tempo de vida das referências. Se precisar trabalhar com referências mutáveis e imutáveis, separe-as em blocos diferentes.
2. Trabalho incorreto com strings: String vs &str
Iniciantes frequentemente confundem String (tipo proprietário, mutável) e &str (referência a uma fatia de string). Isso leva a erros ao passar strings para funções.
Problema: esperar ownership em vez de referência
fn print_length(s: String) {
println!("Comprimento: {}", s.len());
}
fn main() {
let name = "Alice".to_string();
print_length(name);
println!("Nome: {}", name); // ERRO! name foi movida
}
Solução: use &str para parâmetros
fn print_length(s: &str) {
println!("Comprimento: {}", s.len());
}
fn main() {
let name = "Alice".to_string();
print_length(&name); // passamos uma referência
println!("Nome: {}", name); // agora funciona
}
Regra: se a função não precisa assumir a propriedade, use &str. Isso torna o código flexível — você pode passar tanto &String quanto literais de string "texto".
3. Erros com lifetimes (Tempo de Vida)
Quando uma função retorna uma referência, Rust exige que você especifique por quanto tempo essa referência viverá. Iniciantes frequentemente esquecem as anotações de lifetime.
Problema: lifetimes implícitos
fn longest(x: &str, y: &str) -> &str { // ERRO: missing lifetime specifier
if x.len() > y.len() { x } else { y }
}
Solução: adicione um lifetime comum
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let s1 = String::from("curto");
let s2 = "string longa";
let result = longest(&s1, &s2);
println!("A mais longa: {}", result);
}
A anotação 'a informa ao compilador que a referência retornada viverá no máximo tanto quanto o parâmetro de entrada mais curto. Isso garante segurança de memória.
4. Ignorar Option e Result
Rust não possui null. Em vez disso, usa Option<T> para possível ausência de valor e Result<T, E> para tratamento de erros. Iniciantes frequentemente tentam desreferenciar Option sem verificação.
Problema: pânico com unwrap
fn find_user(id: u32) -> Option<String> {
if id == 1 {
Some("Ana".to_string())
}