Errore Rust E0596: cannot borrow as mutable, as it is not declared as mutable
E0596 significa che un pezzo di codice vuole cambiare un valore (gli serve un prestito mutabile, &mut), ma tu gli hai dato solo il permesso di leggerlo. Di solito hai dimenticato mut su un let, oppure una funzione prende & dove le servirebbe &mut. È molto comune, e la riga help: del compilatore ti dice quasi sempre esattamente dove aggiungere mut. Questa pagina ti mostra prima la soluzione e poi te la spiega. Per gli altri errori di questa serie, vedi Errori del compilatore Rust.
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0596]: cannot borrow `tasks` as mutable, as it is not declared as mutable --> src/main.rs:3:5 |3 | tasks.push("write tests"); // ❌ push needs to change `tasks` | ^^^^^ cannot borrow as mutable |help: consider changing this to be mutable |2 | let mut tasks = Vec::new(); | +++Il compilatore dice: «non puoi prendere in prestito tasks come mutabile, perché non è dichiarata mutabile». Questo è il programma che lo produce:
fn main() { let tasks = Vec::new(); tasks.push("write tests"); // ❌ push needs to change `tasks` println!("{tasks:?}");}Vedrai anche una seconda formulazione: «cannot borrow *tasks as mutable, as it is behind a & reference» (non puoi prenderlo in prestito come mutabile, perché sta dietro un riferimento &). Stesso codice di errore, causa leggermente diversa: lì la variabile è un riferimento &, quindi non puoi cambiare ciò a cui punta. Qui sotto trovi entrambi i casi.
Cosa significa
Sezione intitolata “Cosa significa”Guarda i ^^^^^ sotto tasks alla riga 3. Per aggiungere un elemento, Vec::push ha bisogno di un prestito mutabile del vettore: la sua firma è fn push(&mut self, value: T). Ma tasks è stata creata con un semplice let, quindi è di sola lettura. Rust non può darti un &mut su qualcosa che hai detto che non sarebbe mai cambiato.
La riga help: mostra la soluzione: let mut tasks. I +++ indicano i tre caratteri da aggiungere.
Pensa a un documento condiviso con accesso «solo visualizzazione». Puoi leggere ogni parola, ma il pulsante «modifica» è grigio. mut (o &mut) è il modo in cui chiedi l’accesso in modifica.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”In Rust le variabili sono immutabili per default, e anche i riferimenti sono di sola lettura per default. Per cambiare qualcosa devi chiederlo esplicitamente: let mut per una variabile che possiedi, &mut per un riferimento a qualcosa che hai preso in prestito. Vedi Variabili e Prestito (borrowing).
Così il codice è più facile da capire e da fidarsene. Se una funzione prende &Vec<String>, sai già dalla firma che non cambierà la tua lista. Se prende &mut Vec<String>, sai che potrebbe farlo. Niente cambia alle tue spalle.
La stessa regola rende possibili le garanzie di sicurezza più grandi di Rust. Visto che il «può cambiare» è sempre scritto nel codice, il compilatore può assicurarsi che non ci siano mai uno che scrive e uno che legge nello stesso momento (sono E0502 ed E0499). E0596 è il primo passo: devi chiedere di scrivere, prima che qualcuno controlli se scrivere è sicuro.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: Aggiungi mut alla variabile
Sezione intitolata “Soluzione 1: Aggiungi mut alla variabile”Scegli questa quando il valore è tuo e deve cambiare: un Vec in cui fai push, una String a cui aggiungi testo, o una struct di cui chiami un metodo &mut self. Nella maggior parte dei casi è la soluzione giusta.
struct Counter { count: u32,}
impl Counter { fn increment(&mut self) { self.count += 1; }}
fn main() { let mut tasks = Vec::new(); tasks.push("scrivere i test"); tasks.push("correggere il bug"); println!("{tasks:?}");
let mut counter = Counter { count: 0 }; counter.increment(); counter.increment(); println!("Conteggio: {}", counter.count);}Output:
["scrivere i test", "correggere il bug"]Conteggio: 2Senza mut su counter, counter.increment() fallisce con cannot borrow counter as mutable. Chiamare un metodo che prende &mut self prende in prestito in modo mutabile tutta la variabile, quindi la variabile deve essere mut. I metodi che prendono solo &self funzionano benissimo anche senza. Vedi Aggiungere comportamento con i metodi.
Lo stesso vale per un parametro che la funzione possiede: fn shout(s: String) non può chiamare s.push('!'), mentre fn shout(mut s: String) sì.
Soluzione 2: Cambia il parametro in &mut e passa &mut x
Sezione intitolata “Soluzione 2: Cambia il parametro in &mut e passa &mut x”Scegli questa quando una funzione deve cambiare qualcosa che appartiene a chi la chiama. Questa versione produce la formulazione «behind a & reference»:
fn add_task(tasks: &Vec<String>, task: &str) { tasks.push(task.to_string()); // ❌ `tasks` is a `&` reference}error[E0596]: cannot borrow `*tasks` as mutable, as it is behind a `&` reference --> src/main.rs:2:5 |2 | tasks.push(task.to_string()); // ❌ `tasks` is a `&` reference | ^^^^^ `tasks` is a `&` reference, so it cannot be borrowed as mutable |help: consider changing this to be a mutable reference |1 | fn add_task(tasks: &mut Vec<String>, task: &str) { | +++Correggilo in tre punti: il tipo del parametro diventa &mut Vec<String>, la variabile di chi chiama diventa let mut, e la chiamata passa &mut tasks:
fn add_task(tasks: &mut Vec<String>, task: &str) { tasks.push(task.to_string());}
fn main() { let mut tasks = Vec::new(); add_task(&mut tasks, "scrivere i test"); add_task(&mut tasks, "aggiornare la documentazione"); println!("{} attività: {tasks:?}", tasks.len());}Output:
2 attività: ["scrivere i test", "aggiornare la documentazione"]Se cambi il parametro ma continui a passare &tasks, ottieni invece E0308, con la nota types differ in mutability (i tipi differiscono nella mutabilità). Vuol dire solo: scrivi &mut tasks anche nella chiamata. Scrivere &mut nel punto della chiamata è voluto: chiunque legga main vede che add_task può cambiare tasks.
Soluzione 3: Rendi mut la variabile della closure
Sezione intitolata “Soluzione 3: Rendi mut la variabile della closure”Scegli questa quando una closure cambia una variabile che ha catturato. Una closure del genere cambia il proprio stato ogni volta che viene eseguita, quindi per chiamarla serve un prestito mutabile della closure stessa:
fn main() { let mut clicks = 0; let click = || clicks += 1; click(); // ❌ cannot borrow `click` as mutable click(); println!("Clicks: {clicks}");}error[E0596]: cannot borrow `click` as mutable, as it is not declared as mutable --> src/main.rs:3:9 |3 | let click = || clicks += 1; | ^^^^^ ------ calling `click` requires mutable binding due to mutable borrow of `clicks` | | | not mutable4 | click(); // ❌ cannot borrow `click` as mutable | ----- cannot borrow as mutable5 | click(); | ----- cannot borrow as mutable |help: consider changing this to be mutable |3 | let mut click = || clicks += 1; | +++La variabile clicks è già mut. Quello che manca è mut sulla closure:
fn main() { let mut clicks = 0; let mut click = || clicks += 1; click(); click(); click(); println!("Clic: {clicks}");}Output:
Clic: 3Questo sorprende molti, perché la closure non «sembra» una cosa che cambia. Ma dentro tiene un &mut clicks, quindi è una closure FnMut, e per chiamare una FnMut serve mut. Vedi Closure che modificano le variabili catturate e Fn, FnMut e FnOnce.
Soluzione 4: Usa iter_mut() o &mut in un ciclo
Sezione intitolata “Soluzione 4: Usa iter_mut() o &mut in un ciclo”Scegli questa quando scorri una collezione e vuoi cambiare ogni elemento. .iter() e for x in &list ti danno riferimenti &, che sono di sola lettura:
for name in names.iter() { name.push('!'); // ❌ cannot borrow `*name` as mutable, as it is behind a `&` reference}Passa a .iter_mut() o a &mut names (e rendi mut anche names):
fn main() { let mut names = vec![String::from("ferris"), String::from("corro")]; for name in names.iter_mut() { name.push('!'); } println!("{names:?}");
let mut prices = vec![10, 20, 30]; for price in &mut prices { *price += 5; } println!("{prices:?}");}Output:
["ferris!", "corro!"][15, 25, 35]Lo suggerisce anche il compilatore: help: use mutable method ... names.iter_mut(). Lo stesso vale per le closure nelle catene di iteratori: words.iter().for_each(|w| w.push('!')) fallisce, words.iter_mut().for_each(|w| w.push('!')) funziona. Vedi Iteratori.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Costruire un Vec o una String un pezzo alla volta. let list = Vec::new(); e poi list.push(...), oppure let s = String::new(); e poi s.push_str(...). Soluzione: let mut. Se hai già tutti gli elementi all’inizio, let list = vec![a, b, c]; non ha bisogno di mut.
Chiamare un metodo «setter» o di aggiornamento su una struct. account.deposit(50), dove deposit prende &mut self, fallisce se non hai scritto let mut account. Soluzione: let mut account = Account::new();. Vedi Struct.
Funzioni di supporto che prendono & per abitudine. Scrivi fn reset(board: &Board) e poi provi a cambiare board all’interno. Soluzione: cambialo in board: &mut Board e chiama reset(&mut board). Una buona abitudine: scrivi & quando la funzione legge soltanto, &mut quando cambia qualcosa.
Cambiare un valore dentro una HashMap. if let Some(v) = map.get("key") { v.push(..) } fallisce, perché get restituisce un riferimento &. Soluzione: usa map.get_mut("key"), oppure l’API entry. Vedi HashMap.
Errori correlati
Sezione intitolata “Errori correlati”- E0384: cannot assign twice to immutable variable. Lo stesso
mutmancante, ma perx = ...ox += 1invece che per una chiamata di metodo. - E0594: non puoi assegnare a un campo o attraverso un riferimento
&, per esempiop.hp = 5;quandopnon èmut, oppure*x += 1infor x in &v. Stesse soluzioni:let mut, oppure&mut. - E0308: mismatched types,
types differ in mutability, quando passi&xa una funzione che vuole&mut x. - E0502: hai ottenuto l’accesso
&mut, ma qualcos’altro sta ancora leggendo il valore. - E0499: due prestiti
&mutdello stesso valore nello stesso momento. - Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. let scores = Vec::new(); scores.push(10); dà E0596. Qual è la soluzione?
2. Una funzione fn add(list: &Vec<u32>) chiama list.push(1). Cosa cambi?
3. let mut total = 0; let add = |x| total += x; add(5); fallisce. Perché?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0596 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
- References and Borrowing — The Rust Book (in inglese): la parte «Mutable References» spiega
&mute perché bisogna chiederlo. - Method Syntax — The Rust Book (in inglese):
&selfcontro&mut self, e cosa richiede ciascuno a chi chiama. - Closures — The Rust Book (in inglese): come le closure catturano le variabili, e perché una closure che cambia qualcosa deve essere
mut. - Mutability (borrowing) — Rust by Example (in inglese): esempi eseguibili di prestiti
&e&mutfianco a fianco.