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.
A mensagem de erro
Seção intitulada “A mensagem de erro”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` trait7 | greet(name); // aqui name é movido para dentro de greet | ---- value moved here8 | 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 valuehelp: 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;).
O que significa
Seção intitulada “O que significa”Leia os três rótulos de cima para baixo. Eles contam a história toda:
- Linha 6:
nameé umaString, eStringnão éCopy. Então, passá-la para outro lugar a move. - Linha 7:
greet(name)move a string para dentro da função. Agoragreeté a dona dela, e a string é liberada quandogreettermina. - Linha 8: você tenta ler
namede novo, masmainnã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.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”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.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Empreste com &
Seção intitulada “Correção 1: Empreste com &”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!&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.
Correção 2: Clone o valor
Seção intitulada “Correção 2: Clone o valor”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 pontosNome original: FerrisSeja 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.
Correção 3: Derive Copy para tipos pequenos
Seção intitulada “Correção 3: Derive Copy para tipos pequenos”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 }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.
Correção 4: Devolva o ownership
Seção intitulada “Correção 4: Devolva o ownership”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,-- FerrisIsso 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.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”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.
Erros relacionados
Seção intitulada “Erros relacionados”- 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
Stringonde se espera&strdepois de trocar para um empréstimo. - Todos os erros do compilador: a lista completa de páginas de erro.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”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
Para saber mais
Seção intitulada “Para saber mais”- E0382 — Rust error codes index (em inglês): a explicação oficial deste erro, com mais exemplos.
- What Is Ownership? — The Rust Book (em inglês): o capítulo que explica moves,
CloneeCopypasso a passo. - References and Borrowing — The Rust Book (em inglês): como o
&deixa você usar um valor sem movê-lo. - Ownership and moves — Rust by Example (em inglês): exemplos curtos para rodar, com moves e moves parciais.
Copy— documentação da biblioteca padrão (em inglês): quais tipos sãoCopye a diferença entreCopyeClone.