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.