Erro E0384 do Rust: cannot assign twice to immutable variable (sem mut)
E0384 significa que você deu um valor a uma variável e depois tentou dar outro valor a ela, mas a variável não foi declarada com mut. No Rust, uma variável só pode mudar se você disser isso explicitamente. Quase todo mundo esbarra nesse erro na primeira hora com Rust, e a correção costuma ter só três letras: mut. Esta página mostra a correção primeiro e depois explica por que o Rust funciona assim. Para ver os outros erros desta série, acesse Erros do compilador do Rust.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0384]: cannot assign twice to immutable variable `score` --> src/main.rs:4:5 |2 | let score = 10; | ----- first assignment to `score`3 | println!("Score: {score}");4 | score = 15; // ❌ score não é mut | ^^^^^^^^^^ cannot assign twice to immutable variable |help: consider making this binding mutable |2 | let mut score = 10; | +++O compilador diz que não dá para atribuir duas vezes a uma variável imutável (cannot assign twice to immutable variable) e sugere torná-la mutável. Este é o programa que causa o erro:
fn main() { let score = 10; println!("Score: {score}"); score = 15; // ❌ score não é mut println!("New score: {score}");}Se a variável for um parâmetro de função, o texto muda um pouco: “cannot assign to immutable argument” (não é possível atribuir a um argumento imutável). É o mesmo código de erro e a mesma ideia.
O que significa
Seção intitulada “O que significa”O compilador aponta para dois lugares:
- A linha 2, marcada com
first assignment to score(primeira atribuição ascore): é aqui quescorerecebeu seu valor. Como a linha dizlet, e nãolet mut, esse valor é definitivo. - A linha 4, marcada com
^^^^: é aqui que você tentou colocar um valor novo. O Rust não deixa.
A parte help: já mostra a correção: let mut score = 10;. Os +++ marcam os três caracteres que você precisa adicionar.
Pense em let como escrever à caneta e em let mut como escrever a lápis. As duas coisas são válidas. Mas, se você escreveu à caneta, não dá para apagar depois.
A mesma regra vale para +=, -=, *= e companhia. count += 1 também é uma atribuição, então também precisa de mut.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”No Rust, toda variável é imutável por padrão. Para poder mudá-la, você precisa pedir com mut. Isso é explicado na página de Variáveis.
Parece rígido, mas ajuda muito. Quando você lê let total = ..., sabe que total mantém esse valor até o fim do seu escopo. Não precisa vasculhar o resto da função para ver se algo mudou o valor. E, quando você vê let mut, é um sinal claro: “fique de olho nesta, ela muda”.
Isso também pega bugs de verdade. Se você queria mudar uma cópia mas acabou escrevendo no original, ou reaproveitou um nome sem querer, o compilador te para. E o Rust avisa quando uma variável mut nunca muda (variable does not need to be mutable, a variável não precisa ser mutável), então o sinal continua confiável nos dois sentidos.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Adicione mut
Seção intitulada “Correção 1: Adicione mut”Escolha esta quando a variável realmente precisa mudar ao longo do tempo: um contador, um total acumulado, uma pontuação, um estado. É a correção certa na maioria das vezes, e é exatamente o que o compilador sugere.
fn main() { let mut score = 10; println!("Pontuação: {score}"); score = 15; // ✅ agora pode println!("Nova pontuação: {score}");
let mut count = 0; for _ in 0..3 { count += 1; } println!("Contagem: {count}");}Saída:
Pontuação: 10Nova pontuação: 15Contagem: 3O contador é o caso clássico: let count = 0; seguido de count += 1; dentro de um laço dá E0384 na linha do +=. Adicionar mut resolve. Veja Permitindo mudanças com mut.
Correção 2: Use shadowing para um valor novo
Seção intitulada “Correção 2: Use shadowing para um valor novo”Escolha esta quando, no fundo, você não quer mudar a variável. Você quer um valor novo, calculado a partir do anterior, muitas vezes em alguns passos, e que depois não muda mais. Escreva let de novo com o mesmo nome:
fn main() { let price = 20; let price = price + 5; // um `price` novo, feito a partir do anterior let price = price * 2; println!("Preço final: {price}");
let input = " 42 "; let input = input.trim(); // ainda é texto let input: u32 = input.parse().unwrap(); // agora é um número println!("Convertido: {}", input + 1);}Saída:
Preço final: 50Convertido: 43Isso se chama shadowing (sombreamento): cada let cria uma variável totalmente nova que esconde a anterior. Isso traz duas vantagens. O price final é imutável, então nada consegue mudá-lo por acidente mais adiante. E o tipo pode mudar, como input, que passa de &str para u32, algo que mut não consegue fazer. Saiba mais em Shadowing: reaproveitando o mesmo nome.
Uma regra simples: se o valor muda de novo e de novo (em um laço, ao longo do tempo), use mut. Se você está preparando um valor em poucos passos, o shadowing fica bem legível.
Correção 3: Adicione mut a um parâmetro de função
Seção intitulada “Correção 3: Adicione mut a um parâmetro de função”Escolha esta quando uma função quer mudar a própria cópia de um argumento. Parâmetros também são imutáveis, então isto falha:
fn countdown(n: u32) { while n > 0 { println!("{n}..."); n -= 1; // ❌ cannot assign to immutable argument `n` } println!("Liftoff!");}error[E0384]: cannot assign to immutable argument `n` --> src/main.rs:4:9 |4 | n -= 1; // ❌ cannot assign to immutable argument `n` | ^^^^^^ cannot assign to immutable argument |help: consider making this binding mutable |1 | fn countdown(mut n: u32) { | +++Coloque mut antes do nome do parâmetro:
fn countdown(mut n: u32) { while n > 0 { println!("{n}..."); n -= 1; } println!("Decolar!");}
fn main() { let start = 3; countdown(start); println!("start continua valendo {start}");}Saída:
3...2...1...Decolar!start continua valendo 3Repare que start, em main, não mudou. mut n só torna mutável a cópia da própria função; quem chama não precisa de mut e não é afetado. Se você quer que a função mude a variável de quem chamou, receba um &mut u32 e passe &mut start. Veja Funções e Borrowing.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Mudando a variável de um laço for. A variável do laço é uma variável nova e imutável a cada volta:
for i in 0..5 { if i == 2 { i += 1; // ❌ cannot assign twice to immutable variable `i` } println!("{i}");}Correção: raramente você quer mudar o próprio contador do laço. Pule uma volta com continue, use .step_by(2) ou, se precisar de controle total, troque por um laço while com let mut i. (for mut i in 0..5 compila, mas só muda a cópia daquela volta; não pula nada.) Veja break e continue e laços while.
Declarar primeiro, atribuir depois. let total; sem valor é permitido, e você pode atribuir uma vez. Uma segunda atribuição dá E0384:
let total;total = 5;total = 6; // ❌ cannot assign twice to immutable variable `total`Correção: se o valor realmente muda, use let mut total. Se você só queria escolher um valor, calcule tudo em uma expressão: let total = if big_order { 6 } else { 5 };. Veja if / else.
Acumulando um total. let sum = 0; seguido de sum += price; em um laço é o E0384 mais comum de todos. Correção: let mut sum = 0;, ou dispense a variável e escreva let sum: u32 = prices.iter().sum();. Veja Iteradores.
Erros relacionados
Seção intitulada “Erros relacionados”- E0596: cannot borrow as mutable, as it is not declared as mutable (não é possível pegar emprestado como mutável). O mesmo problema de “esqueci o
mut”, mas ao chamar um método que altera o valor, como.push(), em vez de atribuir. - E0594: cannot assign to
p.hp, aspis not declared as mutable. Aparece quando você atribui a um campo de struct (p.hp = 5;) ou escreve através de uma referência&. Correção:let mut p, ou uma referência&mut. Veja Tornando uma struct mutável. - E0502: você tentou mudar um valor enquanto ele estava emprestado para leitura.
- E0382: você usou um valor depois que ele foi movido.
- Todos os erros do compilador: a lista completa de páginas de erro.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Você escreve let lives = 3; e mais tarde lives -= 1;. Por que aparece o E0384?
2. Você quer arrumar name uma única vez (tirar os espaços e passar para maiúsculas) e nunca mais mudá-la. O que fica mais legível?
3. fn halve(n: u32) faz n /= 2; e dá E0384. Qual é a correção que não muda nada para quem chama?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0384 — Rust error codes index (em inglês): a explicação oficial deste erro, com um exemplo.
- Variables and Mutability — The Rust Book (em inglês): o capítulo sobre
let,mut, constantes e shadowing. - Mutability — Rust by Example (em inglês): um exemplo curto e executável de
letversuslet mut. - Freezing — Rust by Example (em inglês): como o shadowing de uma variável mutável por uma imutável “congela” o valor.