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.
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”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.
Cosa significa
Sezione intitolata “Cosa significa”Il compilatore indica due punti:
- La riga 2, segnata con
first assignment to score(prima assegnazione discore): quiscoreha ricevuto il suo valore. Visto che la riga dicelete nonlet mut, quel valore è definitivo. - 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.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”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.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: Aggiungi mut
Sezione intitolata “Soluzione 1: Aggiungi mut”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: 10Nuovo punteggio: 15Conteggio: 3Il 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.
Soluzione 2: Usa lo shadowing per un valore nuovo
Sezione intitolata “Soluzione 2: Usa lo shadowing per un valore nuovo”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: 50Convertito: 43Si 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 3Nota 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).
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”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.
Errori correlati
Sezione intitolata “Errori correlati”- 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, aspis 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
Sezione intitolata “Verifica veloce”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
Per approfondire
Sezione intitolata “Per approfondire”- E0384 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore, con un esempio.
- Variables and Mutability — The Rust Book (in inglese): il capitolo su
let,mut, costanti e shadowing. - Mutability — Rust by Example (in inglese): un breve esempio eseguibile di
letcontrolet mut. - Freezing — Rust by Example (in inglese): come lo shadowing di una variabile mutabile con una immutabile la «congela».