Por que o Rust não permite armazenar uma referência a si mesmo: analisando com exemplos
\n\nImagine 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\nNeste 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\nO problema: tentativa ingênua
\n\nSuponha 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\nstruct 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\nO compilador exibe um erro que confunde muitos iniciantes. Vamos entender o que deu errado.
\n\nPor que o Rust proíbe estruturas autorreferentes?
\n\nA 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.
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\nO que acontece nos bastidores?
\n\nAo escrever este código:
\n\nlet s = String::from("Hello");\nlet first = &s[0..1];\nlet my_struct = MyStruct { data: s, first_char: first };\n\n\nAcontece o seguinte:
\n- \n
sé dono da string "Hello" no heap. \n firsté uma referência a uma parte dessa string. \n - Ao criar a estrutura com
data: s, a propriedade da string é transferida para a estrutura. A string pode ser movida na memória. \n - A referência
firstainda aponta para o endereço antigo — isso é uma referência pendente. O Rust não pode permitir isso. \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\nCenários reais onde estruturas autorreferentes são necessárias
\n\nApesar 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
A boa notícia: existem maneiras seguras de contornar essa limitação!
\n\nSolução 1: Usar índices em vez de referências (a maneira mais simples)
\n\nA 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).
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) -> &