Por que o Rust não precisa de um coletor de lixo, mas ainda assim é seguro?
\n\nQuando desenvolvedores acostumados com Python, Java ou C# ouvem falar do Rust pela primeira vez, muitas vezes ficam assustados com a ausência do tradicional coletor de lixo (GC). "Como assim? — eles pensam. — Sem GC, vamos voltar à era do gerenciamento manual de memória, vazamentos e segmentation fault?"
\n\nSpoiler: não, não vamos voltar. O Rust oferece um terceiro caminho — o sistema de propriedade (ownership), que garante a segurança da memória em tempo de compilação. Nenhum runtime, nenhum GC, nenhum ponteiro solto. Apenas regras claras e um compilador que não deixa você dar um tiro no próprio pé.
\n\nNeste artigo, vamos explorar como a memória funciona no Rust, o que são RAII, propriedade, empréstimo e tempo de vida, e por que tudo isso torna o Rust uma das linguagens de programação de sistema mais confiáveis.
\n\nComo a memória funciona no Rust: pilha e heap
\n\nComo em qualquer linguagem de baixo nível, a memória no Rust é dividida em duas regiões principais:
\n\n- \n
- Pilha (stack) — memória rápida, de tamanho limitado, que opera no princípio LIFO. Aqui são armazenadas variáveis locais, argumentos de funções e endereços de retorno. \n
- Heap — memória mais lenta, mas flexível, usada para dados cujo tamanho é desconhecido em tempo de compilação (por exemplo,
String,Vec,Box). \n
Em C++, você decide quando alocar e liberar o heap (através de new/delete). Em Java ou Python, quem faz isso por você é o coletor de lixo, parando o programa periodicamente. O Rust, por sua vez, usa o sistema de propriedade, que libera a memória automaticamente assim que a variável sai de escopo.
Exemplo: alocação básica de memória
\n\nfn main() {\n let s = String::from("Olá, mundo!"); // alocação de memória no heap\n println!("{}", s);\n} // aqui s sai de escopo, a memória é liberada\n\nObserve: nada de free() ou delete. No final do bloco, o compilador insere implicitamente uma chamada ao destrutor — drop(). Isso é chamado de RAII (Resource Acquisition Is Initialization) — o recurso é adquirido na inicialização e liberado na destruição do objeto.
Propriedade (Ownership): a regra principal do Rust
\n\nO sistema de propriedade no Rust é baseado em três regras simples:
\n\n- \n
- Cada valor no Rust tem exatamente um dono (owner). \n
- Quando o dono sai de escopo, o valor é removido. \n
- O dono pode ser transferido (move) ou emprestado temporariamente (borrow). \n
Parece chato? Vamos ver como isso funciona na prática.
\n\nMovimentação (Move)
\n\nfn main() {\n let s1 = String::from("hello");\n let s2 = s1; // s1 é movido para s2\n // println!("{}", s1); // Erro! s1 não é mais dono dos dados\n println!("{}", s2); // OK\n}\n\nEm C++, essa atribuição resultaria em dois ponteiros para a mesma memória — e em uma dupla liberação. No Rust, o compilador simplesmente proíbe o uso de s1 após a movimentação. Sem vazamentos, sem exclusões duplicadas.
Cópia (Copy)
\n\nTipos simples, como inteiros, valores booleanos e caracteres, implementam a trait Copy. Para eles, a atribuição cria uma cópia completa:
fn main() {\n let x = 42;\n let y = x;\n println!("x = {}, y = {}", x, y); // Ambos estão disponíveis!\n}\n\nPor que isso? Porque i32 é armazenado na pilha, e copiá-lo é apenas copiar alguns bytes. Barato e seguro.
Empréstimo (Borrowing) e referências
\n\nO que fazer se você precisar dar acesso temporário aos dados sem transferir a propriedade? Usar referências. O Rust permite emprestar um valor por referência.
\n\nfn main() {\n let s = String::from(