Pular para o conteúdo

Erro E0499 do Rust: cannot borrow as mutable more than once at a time

O erro E0499 quer dizer que você tentou ter duas referências mutáveis (&mut) para o mesmo valor ao mesmo tempo. É um dos erros mais comuns do borrow checker (o verificador de empréstimos), e a correção costuma ser pequena: muitas vezes basta mudar uma linha de lugar. (Procurando outro erro? Veja a lista de erros do compilador.)

error[E0499]: cannot borrow `balance` as mutable more than once at a time
--> src/main.rs:5:20
|
4 | let deposit = &mut balance;
| ------------ first mutable borrow occurs here
5 | let withdraw = &mut balance;
| ^^^^^^^^^^^^ second mutable borrow occurs here
6 |
7 | *deposit += 50;
| -------------- first borrow later used here

Em português: “não é possível pegar balance emprestado como mutável mais de uma vez ao mesmo tempo”. Os rótulos mostram o primeiro empréstimo mutável (linha 4), o segundo (linha 5) e onde o primeiro ainda é usado (linha 7).

Este é o programa que gera o erro:

fn main() {
let mut balance = 100;
let deposit = &mut balance;
let withdraw = &mut balance;
*deposit += 50;
*withdraw -= 30;
println!("Balance: {balance}");
}

O compilador aponta três lugares e, juntos, eles contam a história toda:

  1. Linha 4: deposit vira um empréstimo mutável de balance.
  2. Linha 5: withdraw tenta virar um segundo empréstimo mutável de balance.
  3. Linha 7: deposit ainda é usado depois da linha 5. Então o primeiro empréstimo continua vivo quando o segundo começa.

O terceiro rótulo é o mais importante. Dois empréstimos &mut só são um problema quando os lifetimes deles se sobrepõem. Se deposit tivesse terminado antes da linha 5, não haveria erro.

Pense num documento compartilhado com uma única chave de “edição”. Quem está com a chave pode alterar o documento. O Rust não faz cópia da chave: a segunda pessoa tem que esperar a primeira devolvê-la.

A regra de borrowing (empréstimo) do Rust é: a qualquer momento você pode ter vários leitores (&) ou um escritor (&mut), nunca mais de um escritor. Uma referência &mut é uma promessa: “enquanto eu existir, ninguém mais mexe neste valor”.

Essa promessa evita bugs reais. Se dois trechos de código pudessem alterar o mesmo valor ao mesmo tempo, um poderia redimensionar um Vec enquanto o outro ainda aponta para a memória antiga. Em outras linguagens, isso é um crash ou dados corrompidos em silêncio. Entre threads, é uma data race (condição de corrida nos dados).

Por causa dessa regra, o compilador tem certeza de que um &mut é o único caminho até os dados enquanto ele vive. Você ganha essa segurança sem nenhum custo em tempo de execução. O preço é que, às vezes, você precisa mostrar ao compilador com mais clareza que seus dois empréstimos não entram em conflito de verdade.

Correção 1: Termine o primeiro empréstimo antes de começar o segundo

Seção intitulada “Correção 1: Termine o primeiro empréstimo antes de começar o segundo”

Tente esta primeiro. Ela resolve a maioria dos E0499. Um empréstimo dura até o seu último uso, e não até o fim do bloco. Então faça todo o trabalho com a primeira referência e só depois crie a segunda:

fn main() {
let mut balance = 100;
let deposit = &mut balance;
*deposit += 50; // último uso de `deposit`: o empréstimo termina aqui
let withdraw = &mut balance; // ✅ agora ninguém mais está pegando emprestado
*withdraw -= 30;
println!("Saldo: {balance}");
}

Saída:

Saldo: 120
▶ Experimente você mesmo

Muitas vezes você nem precisa das variáveis extras: balance += 50; e depois balance -= 30; funcionam do mesmo jeito.

Correção 2: Pegue emprestadas partes que não se sobrepõem

Seção intitulada “Correção 2: Pegue emprestadas partes que não se sobrepõem”

Às vezes você precisa mesmo de dois &mut ao mesmo tempo, mas eles apontam para partes diferentes dos dados. O compilador nem sempre consegue enxergar isso, então você mostra para ele.

Para dois elementos de um Vec ou de um slice, tasks[0] e tasks[1] pegam emprestado o tasks inteiro. Use split_at_mut (em inglês) para cortá-lo em duas metades que com certeza não se sobrepõem:

fn main() {
let mut tasks = vec![String::from("write"), String::from("test")];
// Divide o Vec em duas metades que não se sobrepõem
let (left, right) = tasks.split_at_mut(1);
let first = &mut left[0];
let second = &mut right[0];
std::mem::swap(first, second);
println!("{tasks:?}");
}

Saída:

["test", "write"]
▶ Experimente você mesmo

(Só para trocar dois itens de lugar, tasks.swap(0, 1) é ainda mais simples. O split_at_mut serve para quando você precisa das duas referências para um trabalho de verdade.)

Numa struct, o mesmo erro aparece quando você chama dois métodos que recebem &mut self, como p.name_mut() e p.score_mut(). Cada chamada pega emprestada a struct inteira. Se você pegar emprestados os campos diretamente, o compilador percebe que eles são separados:

struct Player {
name: String,
score: u32,
}
fn main() {
let mut p = Player { name: String::from("Ferris"), score: 0 };
// Pega os campos emprestados diretamente, não por meio de métodos
let name = &mut p.name;
let score = &mut p.score;
name.push_str(", o caranguejo,");
*score += 10;
println!("{} tem {} pontos", p.name, p.score);
}

Saída:

Ferris, o caranguejo, tem 10 pontos
▶ Experimente você mesmo

Correção 3: Use índices em vez de guardar referências

Seção intitulada “Correção 3: Use índices em vez de guardar referências”

Um clássico: você percorre um Vec com iter_mut() e também tenta fazer push nele dentro do loop. O iterador segura um &mut durante o loop inteiro, então o push seria um segundo:

fn main() {
let mut queue = vec![1, 2, 3];
for job in queue.iter_mut() {
*job *= 10;
queue.push(*job); // ❌ E0499: `queue` já está emprestado para o iter_mut()
}
}

Quando o loop precisa alterar a própria coleção, percorra as posições. Um índice é só um número, então não pega nada emprestado:

fn main() {
let mut queue = vec![1, 2, 3];
// Percorre as posições em vez de guardar uma referência para dentro de `queue`
for i in 0..queue.len() {
queue[i] *= 10;
let job = queue[i];
queue.push(job);
}
println!("{queue:?}");
}

Saída:

[10, 20, 30, 10, 20, 30]
▶ Experimente você mesmo

0..queue.len() é calculado uma vez só, antes de o loop começar, então os itens novos não são visitados de novo. Cada empréstimo queue[i] dura só uma linha.

O RefCell muda a verificação de empréstimos do momento da compilação para o tempo de execução. Você pode chamar borrow_mut() sem uma variável mut, desde que dois empréstimos mutáveis nunca estejam vivos ao mesmo tempo:

use std::cell::RefCell;
fn main() {
let balance = RefCell::new(100); // repare: não precisa de `mut`
*balance.borrow_mut() += 50; // pega emprestado, altera, o empréstimo termina no `;`
*balance.borrow_mut() -= 30;
println!("Saldo: {}", balance.borrow());
}

Saída:

Saldo: 120
▶ Experimente você mesmo

Seja sincero com você mesmo: o RefCell não elimina a regra, só a verifica mais tarde. Se você mantiver dois guards de borrow_mut() vivos ao mesmo tempo, o programa compila, mas dá panic quando roda:

use std::cell::RefCell;
fn main() {
let balance = RefCell::new(100);
let mut deposit = balance.borrow_mut();
let mut withdraw = balance.borrow_mut(); // 💥 `deposit` ainda está vivo
*deposit += 50;
*withdraw -= 30;
}
thread 'main' (12345) panicked at src/main.rs:7:32:
RefCell already borrowed

A mensagem diz que a thread principal entrou em panic na linha 7 porque o RefCell já estava emprestado. Então só use RefCell quando os dados forem compartilhados de verdade (por exemplo dentro de Rc<RefCell<T>>) e as Correções 1 a 3 não servirem. Ele não é um jeito de calar o compilador.

  • Guardar &mut num loop. handles.push(&mut counter) dentro de um loop for falha com “mutably borrowed here in the previous iteration of the loop” (emprestado como mutável aqui, na volta anterior do loop). Cada volta do loop acrescenta mais um &mut para o mesmo valor. Correção: guarde índices ou valores próprios, ou altere o valor diretamente no loop.
  • Duas chamadas a get_mut num HashMap. let a = stock.get_mut("apples") e depois let p = stock.get_mut("pears") pegam stock emprestado duas vezes. Correção: termine de usar a antes de chamar get_mut de novo, ou use stock.get_disjoint_mut(["apples", "pears"]) para pegar os dois de uma vez.
  • Duas closures que alteram a mesma variável. let mut inc = || count += 1; e let mut inc2 = || count += 2; seguram, cada uma, um &mut count enquanto existirem. Correção: use uma closure só, ou chame a primeira pela última vez antes de criar a segunda. Veja Closures.

Teste o que aprendeu

1. Quando o Rust mostra o E0499?

2. Você cria let a = &mut v;, usa a pela última vez e depois cria let b = &mut v;. O que acontece?

3. Qual ferramenta permite ter duas referências &mut para elementos diferentes do mesmo slice?

Pontuação: 0 / 3