Estruturas autorreferentes em Rust: tutorial passo a passo

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 permite armazenar uma referência a si mesmo: analisando com exemplos

\n\n

Imagine a situação: você está escrevendo um programa em Rust e precisa criar uma estrutura que armazena alguns dados, além de uma referência a esses mesmos dados. Lógico, não é? Pois é! Mas o compilador Rust de repente exibe um erro, e você fica confuso. Familiar? Essa pergunta é uma das mais populares no Stack Overflow (milhares de visualizações), e hoje vamos analisá-la do início ao fim.

\n\n

Neste tutorial passo a passo, não apenas entenderemos por que não é possível armazenar valor e referência na mesma estrutura Rust, mas também aprenderemos a contornar essa limitação de forma segura. Vamos lá!

\n\n

O problema: tentativa ingênua

\n\n

Suponha que queremos criar uma estrutura que armazena uma string e sua primeira letra (referência ao primeiro caractere). Veja como fica no código:

\n\n
struct MyStruct {\n    data: String,\n    first_char: &str,  // referência a data\n}\n\nfn main() {\n    let s = String::from("Hello");\n    let first = &s[0..1];  // referência ao primeiro caractere\n    \n    let my_struct = MyStruct {\n        data: s,\n        first_char: first,  // tentando armazenar a referência\n    };\n}\n
\n\n

O compilador exibe um erro que confunde muitos iniciantes. Vamos entender o que deu errado.

\n\n

Por que o Rust proíbe estruturas autorreferentes?

\n\n

A raiz do problema está no sistema de ownership (propriedade) e borrowing (empréstimo) do Rust. Quando você move (move) o valor s para o campo data, ele é fisicamente copiado para um novo local na memória. A referência first, no entanto, ainda aponta para o endereço antigo, que agora é inválido.

\n\n

No Rust, cada referência deve ser válida durante toda a sua existência. Estruturas autorreferentes em Rust violam essa regra, pois a referência e o valor vivem no mesmo objeto e, ao mover a estrutura, a referência se torna pendente (dangling reference).

\n\n

O que acontece nos bastidores?

\n\n

Ao escrever este código:

\n\n
let s = String::from("Hello");\nlet first = &s[0..1];\nlet my_struct = MyStruct { data: s, first_char: first };\n
\n\n

Acontece o seguinte:

\n
    \n
  1. s é dono da string "Hello" no heap.
  2. \n
  3. first é uma referência a uma parte dessa string.
  4. \n
  5. Ao criar a estrutura com data: s, a propriedade da string é transferida para a estrutura. A string pode ser movida na memória.
  6. \n
  7. A referência first ainda aponta para o endereço antigo — isso é uma referência pendente. O Rust não pode permitir isso.
  8. \n
\n\n

O Borrow checker do Rust detecta essa potencial insegurança e bloqueia o código em tempo de compilação. Isso não é um bug, mas uma funcionalidade que evita muitos problemas em tempo de execução.

\n\n

Cenários reais onde estruturas autorreferentes são necessárias

\n\n

Apesar da proibição, essas estruturas podem ser úteis. Veja casos típicos:

\n\n
    \n
  • Parsers e AST: quando nós de uma árvore referenciam uns aos outros.
  • \n
  • Motores de jogos: entidades que contêm referências a seus componentes.
  • \n
  • Protocolos de rede: buffers com ponteiros internos.
  • \n
  • Sistemas de plugins: quando um objeto contém uma referência à sua configuração.
  • \n
\n\n

A boa notícia: existem maneiras seguras de contornar essa limitação!

\n\n

Solução 1: Usar índices em vez de referências (a maneira mais simples)

\n\n

A maneira mais simples e segura é armazenar não uma referência, mas um índice ou chave. Em vez de &str, armazene usize (a posição do primeiro caractere).

\n\n
struct MyStruct {\n    data: String,\n    first_char_index: usize,  // armazenamos a posição, não a referência\n}\n\nimpl MyStruct {\n    fn first_char(&self) -> &                

Blogs

Book Recommendations