E0499: Dois Empréstimos Mutáveis no Rust

Como resolver o erro E0499 do Rust: não é possível ter dois empréstimos mutáveis simultâneos. Entenda a regra de exclusividade e veja soluções com exemplos.

E0499: Dois Empréstimos Mutáveis

O erro E0499 ocorre quando você tenta criar duas referências mutáveis para o mesmo dado simultaneamente. O Rust proíbe isso terminantemente: apenas uma referência mutável pode existir por vez.

A Mensagem de Erro

error[E0499]: cannot borrow `dados` as mutable more than once at a time
 --> src/main.rs:5:16
  |
3 |     let ref1 = &mut dados;
  |                --------- first mutable borrow occurs here
4 |
5 |     let ref2 = &mut dados;
  |                ^^^^^^^^^ second mutable borrow occurs here
6 |
7 |     ref1.push(1);
  |     ---- first borrow later used here

O Que Significa

A regra de exclusividade de referências mutáveis é uma das garantias mais importantes do Rust. Ela previne data races em tempo de compilação.

Um data race acontece quando:

  1. Dois ou mais ponteiros acessam o mesmo dado ao mesmo tempo
  2. Pelo menos um deles está escrevendo
  3. Não há mecanismo de sincronização

Se o Rust permitisse duas referências mutáveis, seria possível que uma modificação “quebrasse” as invariantes que a outra referência espera. Por exemplo, um Vec poderia ser realocado por uma referência enquanto a outra aponta para a memória antiga.

A regra é: você pode ter muitos leitores (&T) ou um único escritor (&mut T), mas nunca ambos.

Código com Erro

fn main() {
    let mut dados = vec![1, 2, 3];

    let ref1 = &mut dados;
    let ref2 = &mut dados;  // ERRO: segunda referência mutável

    ref1.push(4);
    ref2.push(5);
}

Cenário comum — dividir um dado entre duas funções que modificam:

fn adicionar(lista: &mut Vec<i32>, valor: i32) {
    lista.push(valor);
}

fn main() {
    let mut numeros = vec![1, 2, 3];
    let ref_mut = &mut numeros;

    // ERRO: `numeros` já está emprestado como mutável
    adicionar(&mut numeros, 4);

    ref_mut.push(5);
}

Outro padrão problemático com structs:

struct Estado {
    contador: i32,
    historico: Vec<i32>,
}

impl Estado {
    fn incrementar(&mut self) {
        self.contador += 1;
        // Isso funciona porque o compilador sabe que são campos diferentes
        self.historico.push(self.contador);
    }
}

Como Resolver

Solução 1: Usar Referências Sequencialmente

Graças ao NLL (Non-Lexical Lifetimes), basta não usar as referências ao mesmo tempo:

fn main() {
    let mut dados = vec![1, 2, 3];

    let ref1 = &mut dados;
    ref1.push(4);
    // ref1 não é mais usado — empréstimo encerrado

    let ref2 = &mut dados;
    ref2.push(5);
    // Funciona perfeitamente
}

Solução 2: Usar Escopos Explícitos

fn main() {
    let mut dados = vec![1, 2, 3];

    {
        let ref1 = &mut dados;
        ref1.push(4);
    } // ref1 sai de escopo

    {
        let ref2 = &mut dados;
        ref2.push(5);
    } // ref2 sai de escopo

    println!("{:?}", dados);
}

Solução 3: Split Borrowing (Empréstimos de Campos Separados)

O Rust permite emprestar campos diferentes de uma struct como mutáveis simultaneamente:

struct Jogador {
    vida: i32,
    pontos: i32,
}

fn main() {
    let mut jogador = Jogador { vida: 100, pontos: 0 };

    let vida = &mut jogador.vida;
    let pontos = &mut jogador.pontos;

    // Funciona! São campos diferentes
    *vida -= 10;
    *pontos += 50;

    println!("Vida: {}, Pontos: {}", vida, pontos);
}

Para slices, use split_at_mut:

fn main() {
    let mut dados = vec![1, 2, 3, 4, 5];

    // Divide o slice em duas partes mutáveis
    let (esquerda, direita) = dados.split_at_mut(3);

    esquerda[0] = 10;
    direita[0] = 40;

    println!("{:?}", dados); // [10, 2, 3, 40, 5]
}

Solução 4: Usar Cell ou RefCell para Mutabilidade Interior

Quando o design exige múltiplos acessos mutáveis, use o padrão de mutabilidade interior:

use std::cell::RefCell;

fn main() {
    let dados = RefCell::new(vec![1, 2, 3]);

    // Múltiplos empréstimos mutáveis em momentos diferentes
    dados.borrow_mut().push(4);
    dados.borrow_mut().push(5);

    println!("{:?}", dados.borrow());
}

Atenção: RefCell move a verificação de empréstimos para tempo de execução. Se você violar as regras, o programa entrará em panic em vez de gerar um erro de compilação. Use com cuidado.

Para cenários multithreaded, use Arc<Mutex<T>>:

use std::sync::{Arc, Mutex};

fn main() {
    let dados = Arc::new(Mutex::new(vec![1, 2, 3]));

    let dados_clone = Arc::clone(&dados);
    dados.lock().unwrap().push(4);
    dados_clone.lock().unwrap().push(5);
}

Veja Também