E0502: Empréstimo Mutável e Imutável no Rust

Como resolver o erro E0502 do Rust: não é possível emprestar como mutável enquanto também emprestado como imutável. Veja explicação e soluções práticas.

E0502: Empréstimo Mutável e Imutável Simultâneo

O erro E0502 ocorre quando você tenta criar um empréstimo mutável (&mut) enquanto já existe um empréstimo imutável (&) ativo para o mesmo dado. Este é um dos pilares de segurança do Rust: você pode ter múltiplas referências imutáveis OU uma única referência mutável, mas nunca ambos ao mesmo tempo.

A Mensagem de Erro

error[E0502]: cannot borrow `dados` as mutable because it is also borrowed as immutable
 --> src/main.rs:5:5
  |
3 |     let referencia = &dados;
  |                      ------ immutable borrow occurs here
4 |
5 |     dados.push(4);
  |     ^^^^^^^^^^^^^ mutable borrow occurs here
6 |
7 |     println!("{:?}", referencia);
  |                      ---------- immutable borrow later used here

O Que Significa

O Rust aplica a regra de exclusão mútua de referências para prevenir data races (condições de corrida) em tempo de compilação. A lógica é simples:

  • Se alguém está lendo um dado (referência imutável &), ninguém pode modificar esse dado ao mesmo tempo.
  • Se alguém está modificando um dado (referência mutável &mut), ninguém mais pode ler nem modificar.

Sem essa regra, seria possível que uma referência imutável “visse” um dado sendo modificado por baixo dos panos, causando comportamento indefinido — algo que nunca acontece em Rust seguro.

O compilador verifica os tempos de vida (lifetimes) das referências. Um empréstimo imutável permanece ativo desde sua criação até seu último uso. Se durante esse período você tenta criar um empréstimo mutável, o compilador rejeita o código.

Código com Erro

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

    // Cria referência imutável
    let primeiro = &numeros[0];

    // ERRO: tenta modificar enquanto `primeiro` ainda é válido
    numeros.push(4);

    // `primeiro` é usado aqui, mantendo o empréstimo imutável ativo
    println!("Primeiro: {}", primeiro);
}

Outro exemplo comum com iteração:

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

    // O `for` cria um empréstimo imutável de `lista`
    for item in &lista {
        if *item == 3 {
            // ERRO: não pode modificar `lista` enquanto itera sobre ela
            lista.push(10);
        }
    }
}

Como Resolver

Solução 1: Reorganizar o Uso das Referências

Graças ao Non-Lexical Lifetimes (NLL), o Rust moderno encerra o empréstimo no último ponto de uso, não no final do escopo. Basta usar a referência imutável antes de criar a mutável:

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

    // Usa a referência imutável ANTES de modificar
    let primeiro = &numeros[0];
    println!("Primeiro: {}", primeiro);
    // `primeiro` não é mais usado — empréstimo imutável encerrado

    // Agora pode modificar livremente
    numeros.push(4);
    println!("{:?}", numeros);
}

Solução 2: Usar Escopos Separados

Quando a reorganização simples não é possível, use blocos para limitar o tempo de vida das referências:

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

    {
        let referencia = &numeros;
        println!("Números: {:?}", referencia);
    } // `referencia` sai de escopo aqui

    // Agora livre para modificar
    numeros.push(4);
}

Solução 3: Coletar Antes, Modificar Depois

Para o caso de modificação durante iteração, colete as mudanças necessárias e aplique-as depois:

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

    // Primeiro, descubra o que precisa mudar
    let precisa_adicionar: Vec<i32> = lista
        .iter()
        .filter(|&&x| x == 3)
        .map(|_| 10)
        .collect();

    // Depois, aplique as mudanças
    lista.extend(precisa_adicionar);
    println!("{:?}", lista); // [1, 2, 3, 4, 5, 10]
}

Ou use retain, drain, ou índices quando precisar modificar durante iteração:

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

    // Remove elementos usando retain (não precisa de empréstimo separado)
    lista.retain(|&x| x != 3);
    println!("{:?}", lista); // [1, 2, 4, 5]
}

Por Que Isso Importa

O exemplo do Vec::push é especialmente relevante: quando um Vec cresce além de sua capacidade, ele realoca a memória. Se existisse uma referência imutável apontando para o endereço antigo, ela se tornaria um dangling pointer — apontando para memória liberada. O Rust previne isso em tempo de compilação.

Veja Também