Salta ai contenuti

Errore Rust E0384: cannot assign twice to immutable variable (manca mut)

E0384 significa che hai dato un valore a una variabile e poi hai provato a dargliene un altro, ma la variabile non era dichiarata con mut. In Rust le variabili non si possono cambiare, a meno che tu non lo dica esplicitamente. Quasi tutti incontrano questo errore nella prima ora con Rust, e la soluzione di solito sono tre lettere: mut. Questa pagina ti mostra prima la soluzione e poi ti spiega perché Rust funziona così. Per gli altri errori di questa serie, vedi Errori del compilatore 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 is not mut
| ^^^^^^^^^^ cannot assign twice to immutable variable
|
help: consider making this binding mutable
|
2 | let mut score = 10;
| +++

Il compilatore dice: «non puoi assegnare due volte la variabile immutabile score». Questo è il programma che lo produce:

fn main() {
let score = 10;
println!("Score: {score}");
score = 15; // ❌ score is not mut
println!("New score: {score}");
}

Se la variabile è un parametro di funzione, il testo cambia un po’ e diventa «cannot assign to immutable argument» (non puoi assegnare a un argomento immutabile). Il codice di errore e l’idea sono gli stessi.

Il compilatore indica due punti:

  1. La riga 2, segnata con first assignment to score (prima assegnazione di score): qui score ha ricevuto il suo valore. Visto che la riga dice let e non let mut, quel valore è definitivo.
  2. La riga 4, segnata con ^^^^: qui hai provato a metterci un valore nuovo. Rust si rifiuta.

La parte help: mostra già la soluzione: let mut score = 10;. I +++ indicano i tre caratteri da aggiungere.

Pensa a let come a scrivere a penna e a let mut come a scrivere a matita. Vanno bene tutti e due. Ma se hai scritto a penna, dopo non puoi cancellare.

La stessa regola vale per +=, -=, *= e simili. Anche count += 1 è un’assegnazione, quindi anche lei ha bisogno di mut.

In Rust ogni variabile è immutabile per default. Per poterla cambiare devi chiederlo con mut. Lo trovi spiegato nella pagina Variabili.

Sembra rigido, ma ti aiuta parecchio. Quando leggi let total = ..., sai che total terrà quel valore fino alla fine del suo scope (ambito). Non devi scorrere il resto della funzione per controllare se qualcosa l’ha cambiato. E quando vedi let mut, è un segnale chiaro: «occhio a questa, cambia».

In più scova bug veri. Se volevi cambiare una copia ma per sbaglio hai scritto sull’originale, o hai riusato un nome per errore, il compilatore ti ferma. E Rust ti avvisa anche delle variabili mut che non cambiano mai (variable does not need to be mutable), così il segnale resta onesto in entrambe le direzioni.

Scegli questa quando la variabile deve davvero cambiare nel tempo: un contatore, un totale progressivo, un punteggio, uno stato. È la soluzione giusta nella maggior parte dei casi, ed è proprio quella che suggerisce il compilatore.

fn main() {
let mut score = 10;
println!("Punteggio: {score}");
score = 15; // ✅ ora è permesso
println!("Nuovo punteggio: {score}");
let mut count = 0;
for _ in 0..3 {
count += 1;
}
println!("Conteggio: {count}");
}

Output:

Punteggio: 10
Nuovo punteggio: 15
Conteggio: 3
▶ Provalo tu

Il contatore è il caso classico: let count = 0; seguito da count += 1; dentro un ciclo dà E0384 sulla riga del +=. Aggiungere mut lo risolve. Vedi Rendere modificabile una variabile con mut.

Scegli questa quando in realtà non vuoi cambiare la variabile. Vuoi un valore nuovo calcolato da quello vecchio, spesso in pochi passaggi, e dopo deve restare così. Scrivi di nuovo let con lo stesso nome:

fn main() {
let price = 20;
let price = price + 5; // un nuovo `price`, costruito dal vecchio
let price = price * 2;
println!("Prezzo finale: {price}");
let input = " 42 ";
let input = input.trim(); // ancora testo
let input: u32 = input.parse().unwrap(); // ora è un numero
println!("Convertito: {}", input + 1);
}

Output:

Prezzo finale: 50
Convertito: 43
▶ Provalo tu

Si chiama shadowing (oscuramento): ogni let crea una variabile tutta nuova che nasconde quella vecchia. Porta con sé due vantaggi. Il price finale è immutabile, quindi più avanti niente può cambiarlo per sbaglio. E il tipo può cambiare, come input che passa da &str a u32, cosa che mut non può fare. Trovi di più in Shadowing: riusare lo stesso nome.

Una regola semplice: se il valore cambia di continuo (in un ciclo, nel tempo), usa mut. Se stai preparando un valore in pochi passaggi, lo shadowing si legge bene.

Soluzione 3: Aggiungi mut a un parametro di funzione

Sezione intitolata “Soluzione 3: Aggiungi mut a un parametro di funzione”

Scegli questa quando una funzione vuole cambiare la propria copia di un argomento. Anche i parametri sono immutabili, quindi questo non compila:

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) {
| +++

Qui rustc dice «non puoi assegnare all’argomento immutabile n». Metti mut davanti al nome del parametro:

fn countdown(mut n: u32) {
while n > 0 {
println!("{n}...");
n -= 1;
}
println!("Decollo!");
}
fn main() {
let start = 3;
countdown(start);
println!("start vale ancora {start}");
}

Output:

3...
2...
1...
Decollo!
start vale ancora 3
▶ Provalo tu

Nota che start in main non è cambiato. mut n rende modificabile solo la copia della funzione; chi chiama non ha bisogno di mut e non viene toccato. Se vuoi che la funzione cambi la variabile di chi chiama, prendi un &mut u32 e passa &mut start. Vedi Funzioni e Prestito (borrowing).

Cambiare la variabile di un ciclo for. La variabile del ciclo è una variabile nuova e immutabile a ogni giro:

for i in 0..5 {
if i == 2 {
i += 1; // ❌ cannot assign twice to immutable variable `i`
}
println!("{i}");
}

Soluzione: raramente vuoi cambiare il contatore stesso. Salta avanti con continue, usa .step_by(2) oppure, se ti serve il controllo completo, passa a un ciclo while con un let mut i. (for mut i in 0..5 compila, ma cambia solo la copia di quel giro; non salta niente.) Vedi break e continue e cicli while.

Dichiarare prima, assegnare dopo. let total; senza valore è permesso, e puoi assegnarlo una volta. Una seconda assegnazione dà E0384:

let total;
total = 5;
total = 6; // ❌ cannot assign twice to immutable variable `total`

Soluzione: se cambia davvero, usa let mut total. Se volevi solo scegliere un valore, calcolalo in un’unica espressione: let total = if big_order { 6 } else { 5 };. Vedi if / else.

Accumulare un totale. let sum = 0; seguito da sum += price; in un ciclo è l’E0384 più comune in assoluto. Soluzione: let mut sum = 0;, oppure fai a meno della variabile e scrivi let sum: u32 = prices.iter().sum();. Vedi Iteratori.

  • E0596: cannot borrow as mutable, as it is not declared as mutable. Lo stesso problema del «hai dimenticato mut», ma quando chiami un metodo che modifica, come .push(), invece di assegnare.
  • E0594: cannot assign to p.hp, as p is not declared as mutable. Lo vedi quando assegni a un campo di una struct (p.hp = 5;) o scrivi attraverso un riferimento &. Soluzione: let mut p, oppure un riferimento &mut. Vedi Rendere mutabile una struct.
  • E0502: hai provato a cambiare un valore mentre era preso in prestito in lettura.
  • E0382: hai usato un valore dopo che è stato spostato.
  • Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.

Verifica veloce

1. Scrivi let lives = 3; e più avanti lives -= 1;. Perché ottieni E0384?

2. Vuoi sistemare name una volta sola (togliere gli spazi e renderlo maiuscolo) e non cambiarlo più. Cosa si legge meglio?

3. fn halve(n: u32) fa n /= 2; e ottiene E0384. Qual è la soluzione che lascia invariato chi chiama?

Punteggio: 0 / 3