O que é um coletor de lixo e por que o Rust o abandonou?
\n\nSe você vem do mundo Python, Java ou C#, está acostumado a ter a memória gerenciada por um coletor de lixo (Garbage Collector, GC). É um "limpador" invisível que verifica periodicamente quais objetos não são mais usados e libera a memória que ocupam. Conveniente? Sim. Mas há nuances:
\n\n- \n
- Pausas imprevisíveis. O GC pode ser acionado no pior momento possível — quando seu código está processando uma requisição importante ou renderizando um quadro em um jogo. \n
- Sobrecarga de memória. O GC frequentemente requer memória adicional para seu funcionamento. \n
- Complexidade em sistemas de tempo real. Em sistemas operacionais, drivers ou dispositivos embarcados, as pausas do GC são inaceitáveis. \n
O Rust oferece uma abordagem radicalmente diferente. Em vez de um coletor de lixo, utiliza-se um sistema de propriedade (ownership) que funciona em tempo de compilação. O compilador decide quando a memória deve ser liberada e faz isso sem qualquer sobrecarga em tempo de execução. Vamos entender como isso funciona.
\n\nSistema de Propriedade (Ownership) — a base do gerenciamento de memória
\n\nNo Rust, cada valor tem exatamente um proprietário — a variável à qual esse valor pertence. Quando o proprietário sai de escopo (scope), a memória é automaticamente liberada. Nada de coletor de lixo, nada de contagem de referências — apenas o compilador e regras rigorosas.
\n\nRegras de propriedade no Rust
\n\nAqui estão as três principais regras que o compilador verifica em tempo de compilação:
\n\n- \n
- Cada valor no Rust tem um proprietário. \n
- Quando o proprietário sai de escopo, o valor é removido (drop). \n
- O valor pode ser transferido para outro proprietário (move) ou emprestado (borrow). \n
fn main() {\n let s1 = String::from("Hello"); // s1 — proprietário da string\n let s2 = s1; // A propriedade é TRANSFERIDA para s2 (move)\n // println!("{}", s1); // Erro! s1 não é mais o proprietário da string\n println!("{}", s2); // Ok: s2 é o novo proprietário\n} // Aqui, tanto s1 quanto s2 saem de escopo,\n // mas a memória é liberada apenas uma vez — para s2\n\nEm Python ou Java, você simplesmente criaria outra referência ao objeto, e o GC cuidaria dela depois. No Rust — sem surpresas: o compilador garante que a memória será liberada exatamente uma vez.
\n\nEmpréstimo (Borrowing) — como usar dados sem transferir a propriedade
\n\nFrequentemente precisamos "emprestar" temporariamente o acesso aos dados, sem tirá-los do proprietário. Para isso, o Rust possui referências (references). Elas são de dois tipos:
\n\n- \n
- Referências imutáveis (
&T): permitem ler os dados, mas não alterá-los. \n - Referências mutáveis (
&mut T): permitem ler e alterar, mas apenas se não houver outras referências ativas. \n
fn main() {\n let mut text = String::from("Rust");\n \n let r1 = &text; // referência imutável\n let r2 = &text; // outra referência imutável — permitido\n println!("{} {}", r1, r2);\n \n let r3 = &mut text; // referência mutável — funciona, pois r1 e r2 não são mais usadas\n r3.push_str(" rocks!");\n println!("{}", r3);\n \n // Mas assim não pode:\n // let r1 = &text;\n // let r3 = &mut text; // Erro! Não é possível ter referências mutável e imutável simultaneamente\n}\n\nO compilador Rust não é apenas um analisador estático, é um professor rigoroso, mas justo. Ele não deixa você dar um tiro no próprio pé. Se você tentar criar uma referência mutável enquanto existem referências imutáveis — receberá um erro de compilação. Este é o trabalho do borrow checker — o mecanismo que substitui o