Pular para o conteúdo

Erro E0382 do Rust: borrow of moved value (valor movido)

O E0382 quer dizer que você entregou um valor (você o moveu) e depois tentou usá-lo de novo. É o erro mais comum na primeira semana com Rust e quase sempre se resolve com uma linha. Esta página mostra a correção primeiro e depois explica o que está acontecendo. Para os outros erros desta série, veja Erros do compilador Rust.

error[E0382]: borrow of moved value: `name`
--> src/main.rs:8:25
|
6 | let name = String::from("Ferris");
| ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
7 | greet(name); // aqui name é movido para dentro de greet
| ---- value moved here
8 | println!("Goodbye, {name}!"); // ❌ name não existe mais
| ^^^^ value borrowed here after move
|
note: consider changing this parameter type in function `greet` to borrow instead if owning the value isn't necessary
--> src/main.rs:1:16
|
1 | fn greet(name: String) {
| ----- ^^^^^^ this parameter takes ownership of the value
help: consider cloning the value if the performance cost is acceptable
|
7 | greet(name.clone()); // aqui name é movido para dentro de greet
| ++++++++

Em português: “empréstimo de um valor movido”. O compilador avisa que name foi movido na linha 7 e lido na linha 8, sugere mudar o parâmetro de greet para um empréstimo e, como alternativa, clonar o valor.

Este é o programa que gera o erro:

fn greet(name: String) {
println!("Hello, {name}!");
}
fn main() {
let name = String::from("Ferris");
greet(name); // aqui name é movido para dentro de greet
println!("Goodbye, {name}!"); // ❌ name não existe mais
}

Você também pode ver a frase “use of moved value” (uso de um valor movido). É o mesmo erro. O Rust diz “borrow” quando a linha seguinte só lê o valor (como println!) e “use” quando tenta movê-lo de novo (como let other = name;).

Leia os três rótulos de cima para baixo. Eles contam a história toda:

  1. Linha 6: name é uma String, e String não é Copy. Então, passá-la para outro lugar a move.
  2. Linha 7: greet(name) move a string para dentro da função. Agora greet é a dona dela, e a string é liberada quando greet termina.
  3. Linha 8: você tenta ler name de novo, mas main não é mais dona dela.

Pense em entregar a única chave da sua casa para um amigo. Depois que você a entregou, não dá para abrir a porta com ela, mesmo lembrando que ela já foi sua.

Em Rust, todo valor tem exatamente um dono. Quando o dono vai embora, o valor é liberado. É assim que o Rust libera memória sem coletor de lixo (garbage collector). As regras de ownership (posse) estão explicadas na página Ownership.

Se duas variáveis fossem donas da mesma String, as duas tentariam liberar a mesma memória no final. Isso é um “double free” (liberação dupla), um bug real que derruba programas em C e C++. O Rust evita isso fazendo com que atribuições e chamadas de função movam o valor, para que sempre exista um único dono.

Tipos pequenos e simples, como i32, bool e char, são Copy: passá-los faz uma cópia barata, e você nunca vê o E0382 com eles. Tipos que guardam memória no heap, como String, Vec e a maioria das structs, são movidos.

Escolha esta quando a função só precisa olhar o valor. Na maioria das vezes, é a correção certa. Troque o parâmetro por uma referência e passe &name:

fn greet(name: &str) {
println!("Hello, {name}!");
}
fn main() {
let name = String::from("Ferris");
greet(&name); // empresta, não entrega
println!("Goodbye, {name}!"); // ✅ continua sendo nossa
}

Saída:

Hello, Ferris!
Goodbye, Ferris!
▶ Experimente você mesmo

&String também funcionaria, mas &str é a escolha mais comum porque aceita também literais de string. Veja Borrowing e Strings. Se a função precisar alterar o valor, use &mut.

Escolha esta quando o outro código precisa mesmo de uma cópia própria, por exemplo para guardar numa struct ou num Vec, e você ainda precisa do original. .clone() faz uma cópia completa e independente:

struct Player {
name: String,
score: u32,
}
fn main() {
let name = String::from("Ferris");
let player = Player { name: name.clone(), score: 0 };
println!("{} começa com {} pontos", player.name, player.score);
println!("Nome original: {name}");
}

Saída:

Ferris começa com 0 pontos
Nome original: Ferris
▶ Experimente você mesmo

Seja sincero com você mesmo: clonar uma string curta ou um Vec pequeno é barato e não tem problema nenhum, principalmente enquanto você está aprendendo. Só se preocupe quando clonar dados grandes dentro de um loop que roda muitas vezes. Se um empréstimo (Correção 1) resolver, prefira ele.

Escolha esta para structs pequenas feitas só de valores simples, como coordenadas, cores ou IDs. Acrescente #[derive(Clone, Copy)] e a struct passa a ser copiada em vez de movida:

#[derive(Debug, Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
fn show(p: Point) {
println!("({}, {})", p.x, p.y);
}
fn main() {
let start = Point { x: 0, y: 3 };
show(start); // copia start
show(start); // ✅ funciona de novo
println!("{:?}", start);
}

Saída:

(0, 3)
(0, 3)
Point { x: 0, y: 3 }
▶ Experimente você mesmo

Copy só funciona se todos os campos também forem Copy. Uma struct com um campo String ou Vec não pode ser Copy; nesses casos, use Clone e chame .clone(). Mais sobre isso em Traits comuns.

Escolha esta quando uma função recebe um valor, altera e você quer o resultado. A função retorna o valor, e você o guarda de novo:

fn add_signature(mut letter: String) -> String {
letter.push_str("\n-- Ferris");
letter // devolve o valor
}
fn main() {
let letter = String::from("Querido Rustacean,");
let letter = add_signature(letter); // pega de volta
println!("{letter}");
}

Saída:

Querido Rustacean,
-- Ferris
▶ Experimente você mesmo

Isso funciona bem para funções no estilo “builder”. Para uma alteração simples no próprio valor, fn add_signature(letter: &mut String) costuma ser mais fácil de ler.

Um loop for sobre um Vec. for task in tasks move o vetor inteiro para dentro do loop, então você não pode usar tasks depois:

for task in tasks {
println!("Doing: {task}");
}
println!("{} tasks done", tasks.len()); // ❌ `tasks` moved due to this implicit call to `.into_iter()`

A mensagem diz que tasks foi movido por causa de uma chamada implícita a .into_iter(). Correção: percorra um empréstimo, for task in &tasks. Veja For em Rust e Iteradores.

Colocar um valor num Vec ou numa struct. names.push(name) e Order { customer } movem o valor para dentro. Usar name ou customer depois disso falha. Correção: leia o valor de volta da nova casa dele (names[0], order.customer), faça o resto do trabalho antes do move ou coloque um .clone().

Mover dentro de um loop. Se log.push(message) estiver dentro de um loop, a primeira volta move message e a segunda não tem mais nada. O compilador diz value moved here, in previous iteration of loop (valor movido aqui, numa volta anterior do loop). Correção: use message.clone() no push, ou crie o valor dentro do loop para que cada volta tenha um novo.

Threads e closures move. thread::spawn(move || ...) move todas as variáveis capturadas para a nova thread. Correção: clone os dados antes de criar a thread, ou compartilhe com Arc. Veja Concorrência e Estado compartilhado.

  • E0502: você pegou um valor emprestado e depois tentou alterá-lo enquanto o empréstimo ainda estava em uso.
  • E0499: dois empréstimos mutáveis do mesmo valor ao mesmo tempo.
  • E0106: falta um lifetime, algo comum quando você retorna uma referência para evitar um move.
  • E0308: tipos incompatíveis, por exemplo passar String onde se espera &str depois de trocar para um empréstimo.
  • Todos os erros do compilador: a lista completa de páginas de erro.

Teste o que aprendeu

1. Você chama save(report), sendo report uma String, e depois imprime report. Por que aparece o E0382?

2. Uma função só precisa ler um Vec<String>. Qual costuma ser a melhor correção?

3. Qual struct pode usar #[derive(Clone, Copy)]?

Pontuação: 0 / 3