E0505: Mover Valor Enquanto Emprestado
O erro E0505 surge quando você tenta mover (transferir o ownership de) um valor que ainda tem referências ativas apontando para ele. É a interseção de dois conceitos fundamentais do Rust: ownership e borrowing.
A Mensagem de Erro
error[E0505]: cannot move out of `dados` because it is borrowed
--> src/main.rs:5:15
|
3 | let ref_dados = &dados;
| ------ borrow of `dados` occurs here
4 |
5 | let novo = dados;
| ^^^^^ move out of `dados` occurs here
6 |
7 | println!("{:?}", ref_dados);
| --------- borrow later used here
O Que Significa
Quando você cria uma referência (&dados), o Rust garante que essa referência permaneça válida enquanto estiver em uso. Se o valor fosse movido para outra variável enquanto a referência existe, a referência apontaria para memória inválida — um dangling reference.
O Rust previne esta situação em tempo de compilação: se existe qualquer referência ativa (empréstimo) para um valor, esse valor não pode ser movido.
Pense assim: se você emprestou seu carro para alguém, não pode vendê-lo enquanto a pessoa está usando. Primeiro ela tem que devolver (o empréstimo precisa terminar), depois você pode vender (mover o ownership).
Código com Erro
fn main() {
let dados = String::from("informação importante");
// Cria uma referência para `dados`
let referencia = &dados;
// ERRO: tenta mover `dados` para `consumir`
consumir(dados);
// A referência ainda seria usada aqui
println!("Ref: {}", referencia);
}
fn consumir(s: String) {
println!("Consumido: {}", s);
}
Outro cenário frequente com structs:
struct Servidor {
nome: String,
conexoes: Vec<String>,
}
fn main() {
let servidor = Servidor {
nome: String::from("api-01"),
conexoes: vec![],
};
let nome_ref = &servidor.nome;
// ERRO: mover `servidor` invalida `nome_ref`
let outro = servidor;
println!("Nome: {}", nome_ref);
}
Como Resolver
Solução 1: Terminar o Empréstimo Antes do Move
Reorganize o código para que a referência não seja mais usada antes de mover o valor:
fn main() {
let dados = String::from("informação importante");
// Usa a referência ANTES de mover
let referencia = &dados;
println!("Ref: {}", referencia);
// Empréstimo encerrado (último uso)
// Agora pode mover
consumir(dados);
}
fn consumir(s: String) {
println!("Consumido: {}", s);
}
Solução 2: Usar Referências em Vez de Move
Se a função não precisa do ownership, passe uma referência:
fn exibir(s: &str) {
println!("Exibindo: {}", s);
}
fn main() {
let dados = String::from("informação importante");
let referencia = &dados;
// Passa referência — sem move
exibir(&dados);
println!("Ref: {}", referencia);
}
Solução 3: Clonar o Valor
Se você precisa tanto do empréstimo quanto do move, clone o valor:
fn consumir(s: String) {
println!("Consumido: {}", s);
}
fn main() {
let dados = String::from("informação importante");
let referencia = &dados;
// Clona para mover a cópia, mantendo o original
consumir(dados.clone());
println!("Ref: {}", referencia);
}
Solução 4: Usar Blocos para Controlar Escopos
fn consumir(s: String) {
println!("Consumido: {}", s);
}
fn main() {
let dados = String::from("informação importante");
{
let referencia = &dados;
println!("Ref: {}", referencia);
} // referencia sai de escopo aqui
consumir(dados); // move liberado
}
Diferença Entre E0505 e E0382
- E0382 (Uso após move): Você moveu o valor e tenta usá-lo depois.
- E0505 (Mover enquanto emprestado): Você tenta mover um valor que ainda tem referências ativas.
Ambos protegem contra acesso a memória inválida, mas por ângulos diferentes. O E0505 foca na existência de referências pendentes, enquanto o E0382 foca na tentativa de usar um valor que já não está mais disponível.