Por que o Rust reclama de borrow e como corrigir isso? Análise completa

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

Introdução: O momento em que o Rust «não te entende»

\n

Você está programando em Rust. Tudo vai bem. O compilador fica em silêncio. Você se sente um mestre da memória. E então — bum! — aparece ela:

\n
error[E0507]: cannot move out of borrowed content\n  --> src/main.rs:10:9\n   |\n10 |         let value = self.data;\n   |                     ^^^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait\n
\n

Ou sua parente próxima:

\n
error[E0507]: cannot move out of `*self` which is behind a shared reference\n
\n

Se você é iniciante, isso parece uma maldição mágica. Se é experiente — sabe que é apenas mais uma lição do Borrow Checker (verificador de empréstimos).

\n

Neste artigo, vamos analisar o que significam esses erros, por que o Rust é tão rigoroso e — o mais importante — como corrigi-los com exemplos de código reais. Não é teoria chata, mas um guia prático para sobreviver no mundo da propriedade e do empréstimo.

\n\n

Resumindo o essencial: Propriedade, empréstimo e «movimentação»

\n

Antes de nos aprofundarmos, vamos lembrar os três pilares do Rust:

\n
    \n
  • Propriedade (Ownership): Cada valor no Rust tem exatamente um dono.
  • \n
  • Empréstimo (Borrowing): Você pode «emprestar» um valor por referência (&T — imutável, &mut T — mutável), sem tomar a propriedade.
  • \n
  • Movimentação (Move): Quando você atribui um valor a uma nova variável ou o passa para uma função sem &, a propriedade é movida. A variável original se torna inválida.
  • \n
\n

O erro cannot move out of borrowed content ocorre exatamente quando você tenta mover um valor para o qual você tem apenas uma referência (&T). O Rust proíbe isso, porque após a movimentação a referência se tornará «pendente» (dangling pointer). E o Rust, como você sabe, não perdoa essas coisas.

\n\n

Cenário 1: Tentativa de mover um campo de estrutura via &self

\n

O caso mais comum. Você tem uma estrutura e quer retornar um de seus campos a partir de um método que recebe &self.

\n\n

Código com erro

\n
struct User {\n    name: String,\n    age: u32,\n}\n\nimpl User {\n    // Tentando entregar o name "definitivamente"\n    fn give_name(&self) -> String {\n        self.name // ERRO!\n    }\n}\n\nfn main() {\n    let user = User { name: "Alice".to_string(), age: 30 };\n    let name = user.give_name();\n    println!("{}", name);\n}\n
\n

O compilador dirá:

\n
error[E0507]: cannot move out of `self.name` which is behind a shared reference\n
\n

Por quê? self é &User. Nós não possuímos self, apenas o emprestamos. Tentar mover self.name (tipo String, que não implementa Copy) significa que, após a chamada do método, o dono (user) perderá seu campo. Mas a referência &self ainda existe! O Rust nos protege de um desastre.

\n\n

Como corrigir?

\n

Existem três caminhos principais, dependendo da sua tarefa.

\n\n

Maneira 1: Clonagem (a mais simples, mas nem sempre eficiente)

\n

Se você precisa exatamente do valor, e não de uma referência, e está disposto a pagar pela cópia:

\n
fn give_name(&self) -> String {\n    self.name.clone() // Funciona! Cria uma cópia.\n}\n
\n

Desvantagem: clone() para String faz uma cópia completa dos dados no heap. Em strings grandes, isso é caro.

\n\n

Maneira 2: Retorno de referência (alteramos a assinatura)

\n

Se o código chamador precisar apenas de uma referência ao campo:

\n
fn give_name(&self) -> &String {\n    &self.name // Retornamos uma referência, sem movimentação\n}\n
\n

Agora não movemos, mas emprestamos. Tudo justo. O código chamador receberá

Blogs

Book Recommendations