Pular para o conteúdo

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.

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 compilador aponta para dois lugares:

  1. A linha 2, marcada com first assignment to score (primeira atribuição a score): é aqui que score recebeu seu valor. Como a linha diz let, e não let mut, esse valor é definitivo.
  2. 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.

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.

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: 10
Nova pontuação: 15
Contagem: 3
▶ Experimente você mesmo

O 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.

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: 50
Convertido: 43
▶ Experimente você mesmo

Isso 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 3
▶ Experimente você mesmo

Repare 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.

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.

  • 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, as p is 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

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