Memória e coletor de lixo em Rust: como uma linguagem sem GC gerencia recursos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Por que o Rust não precisa de um coletor de lixo, mas ainda assim é seguro?

\n\n

Quando 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\n

Spoiler: 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\n

Neste 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\n

Como a memória funciona no Rust: pilha e heap

\n\n

Como 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
\n\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.

\n\n

Exemplo: alocação básica de memória

\n\n
fn 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\n

Observe: 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.

\n\n

Propriedade (Ownership): a regra principal do Rust

\n\n

O sistema de propriedade no Rust é baseado em três regras simples:

\n\n
    \n
  1. Cada valor no Rust tem exatamente um dono (owner).
  2. \n
  3. Quando o dono sai de escopo, o valor é removido.
  4. \n
  5. O dono pode ser transferido (move) ou emprestado temporariamente (borrow).
  6. \n
\n\n

Parece chato? Vamos ver como isso funciona na prática.

\n\n

Movimentação (Move)

\n\n
fn 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\n

Em 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.

\n\n

Cópia (Copy)

\n\n

Tipos simples, como inteiros, valores booleanos e caracteres, implementam a trait Copy. Para eles, a atribuição cria uma cópia completa:

\n\n
fn main() {\n    let x = 42;\n    let y = x;\n    println!("x = {}, y = {}", x, y); // Ambos estão disponíveis!\n}
\n\n

Por que isso? Porque i32 é armazenado na pilha, e copiá-lo é apenas copiar alguns bytes. Barato e seguro.

\n\n

Empréstimo (Borrowing) e referências

\n\n

O 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\n
fn main() {\n    let s = String::from(                

Blogs

Book Recommendations