Errore Rust E0597: borrowed value does not live long enough (vive troppo poco)
E0597 significa: hai tenuto in vita un riferimento (&x) più a lungo del valore x stesso. Il valore viene eliminato alla fine del suo blocco, ma qualcosa fuori da quel blocco vuole ancora usare il riferimento. È un errore molto comune, soprattutto con blocchi interni, cicli e struct, e la soluzione di solito è spostare una riga o salvare un valore posseduto invece di un riferimento. (Questa pagina fa parte di Errori del compilatore Rust spiegati.)
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0597]: `x` does not live long enough --> src/main.rs:5:13 |4 | let x = 5; | - binding `x` declared here5 | r = &x; // r borrows x | ^^ borrowed value does not live long enough6 | } // x is dropped here | - `x` dropped here while still borrowed7 | println!("r: {r}"); // ❌ r would point at nothing | - borrow later used hereIl compilatore dice: «x non vive abbastanza a lungo». Questo è il programma che lo produce:
fn main() { let r; { let x = 5; r = &x; // r borrows x } // x is dropped here println!("r: {r}"); // ❌ r would point at nothing}I commenti nel codice dicono: r prende in prestito x; qui x viene eliminato; r punterebbe al nulla.
Cosa significa
Sezione intitolata “Cosa significa”Le quattro etichette raccontano la storia in ordine:
- Riga 4:
xviene creato dentro il blocco interno{ ... }. - Riga 5:
rprende in prestitox. Fin qui tutto bene. - Riga 6: il blocco finisce, quindi
xviene eliminato (drop). Il prestito però è ancora vivo. - Riga 7:
rviene usato, ma il valore a cui punta non c’è più.
Pensa di annotarti il numero della camera d’albergo di un amico. È utile finché lui alloggia lì. Quando fa il check-out, il numero è ancora sul tuo foglietto, ma ora porta a una stanza vuota, o a quella di uno sconosciuto. Rust non ti lascia tenere un foglietto che porta a una stanza vuota.
Vedrai anche l’etichetta breve «borrowed value does not live long enough» (il valore preso in prestito non vive abbastanza a lungo). È lo stesso errore; il titolo nomina solo la variabile.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”Un riferimento non deve mai vivere più a lungo del valore a cui punta. Se succedesse, avresti un riferimento pendente (dangling reference): un puntatore a memoria già liberata, che ora potrebbe contenere qualcos’altro. In C e C++ è una fonte classica di crash e di falle di sicurezza («use after free»).
Ogni valore in Rust viene eliminato quando il suo proprietario esce dallo scope (ambito), di solito alla } di chiusura del blocco in cui è stato dichiarato. Il borrow checker confronta due intervalli: quanto vive il valore e per quanto tempo viene usato il riferimento. Se il riferimento viene usato dopo la } del valore, ottieni E0597. Vedi Scope, Prestito (borrowing) e Lifetime.
Nota di cosa il compilatore non si lamenta: del prestito di x alla riga 5. Il problema è solo l’uso successivo alla riga 7. Togli l’ultima riga e il programma compila. È un indizio utile per le soluzioni qui sotto: o fai vivere più a lungo il valore, o fai finire prima l’uso del riferimento.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: Sposta il valore in uno scope esterno
Sezione intitolata “Soluzione 1: Sposta il valore in uno scope esterno”Scegli questa quando il valore serve davvero per tutto il tempo in cui serve il riferimento. È la soluzione più comune: dichiara il valore prima del blocco (fuori), così vive almeno quanto il riferimento.
fn main() { let scores = vec![72, 95, 88]; // ora vive fino alla fine di main let best; { best = scores.iter().max().unwrap(); } println!("Punteggio migliore: {best}"); // ✅ scores è ancora vivo}Output:
Punteggio migliore: 95Regola pratica: il proprietario va dichiarato prima di chi prende in prestito, e deve sopravvivergli. Se vedi un valore creato dentro un blocco o un ciclo e un riferimento a lui usato fuori, porta fuori il valore.
Soluzione 2: Salva valori posseduti invece di riferimenti
Sezione intitolata “Soluzione 2: Salva valori posseduti invece di riferimenti”Scegli questa quando raccogli valori creati dentro un ciclo. Ogni giro del ciclo crea una nuova String e la elimina alla fine del giro, quindi un Vec<&str> fuori dal ciclo non può tenere riferimenti a quelle stringhe:
fn main() { let mut names: Vec<&str> = Vec::new(); for i in 1..=3 { let name = format!("player{i}"); names.push(&name); // ❌ `name` does not live long enough } println!("{names:?}");}La soluzione è lasciare che sia il Vec a possedere le stringhe. Cambia Vec<&str> in Vec<String> e inserisci la String stessa (così viene spostata nel vettore e vive quanto lui):
fn main() { let mut names: Vec<String> = Vec::new(); for i in 1..=3 { let name = format!("giocatore{i}"); names.push(name); // sposta la String nel Vec } println!("{names:?}");}Output:
["giocatore1", "giocatore2", "giocatore3"]La stessa idea vale per struct e HashMap: se i dati vengono creati in uno scope più piccolo del contenitore, salva String (o Vec<T>) invece di &str (o &[T]). Per chi comincia, i campi posseduti sono quasi sempre la scelta giusta. Vedi Stringhe e Vettori.
Soluzione 3: Clonalo
Sezione intitolata “Soluzione 3: Clonalo”Scegli questa quando ti serve continuare a usare anche l’originale, o quando ne hai solo un prestito. Una struct che contiene un riferimento (Label<'a> { text: &'a str }) non può vivere più a lungo della stringa a cui punta. Dai alla struct un campo String posseduto e salvaci una copia:
struct Label { text: String, // posseduto, non &str}
fn main() { let label; { let title = String::from("Posta in arrivo"); label = Label { text: title.clone() }; println!("Dentro il blocco: {title}"); } println!("Etichetta: {}", label.text); // ✅ label possiede la sua copia}Output:
Dentro il blocco: Posta in arrivoEtichetta: Posta in arrivoSe dopo non ti serve più title, salta il clone e spostalo: Label { text: title }. Clonare una stringa corta costa poco e va benissimo. Se hai solo un &str, usa .to_string() per ottenere una copia posseduta.
Soluzione 4: Riorganizza il codice perché il prestito finisca in tempo
Sezione intitolata “Soluzione 4: Riorganizza il codice perché il prestito finisca in tempo”Scegli questa quando il valore deve vivere poco. Invece di far vivere più a lungo il valore, usa il riferimento finché il valore è ancora vivo. Capita spesso con le funzioni che restituiscono un riferimento, come il classico esempio longest:
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() >= b.len() { a } else { b }}
fn main() { let first = String::from("Ferris il granchio"); { let second = String::from("Corro"); let winner = longest(&first, &second); println!("Nome più lungo: {winner}"); // ✅ usato prima che second venga eliminato }}Output:
Nome più lungo: Ferris il granchioSe winner è dichiarato fuori dal blocco e stampato dopo, ottieni E0597 per second, anche se per caso first è più lungo. La firma dice che il risultato può prendere in prestito da uno qualsiasi dei due argomenti, quindi entrambi devono sopravvivergli. Se il risultato ti serve davvero più avanti, salvalo come valore posseduto: let winner = longest(&first, &second).to_string();.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Una closure salvata fuori dal blocco. Una closure che usa name lo prende in prestito. Se la closure vive più a lungo di name, ottieni E0597 con un’etichetta in più, value captured here (valore catturato qui):
let say_hello;{ let name = String::from("Ferris"); say_hello = || println!("Hello, {name}!"); // ❌ `name` does not live long enough}say_hello();Soluzione: scrivi move || ..., così la closure prende l’ownership (possesso) di name. Vedi Closure.
I thread. Prendere in prestito una variabile locale dentro thread::spawn(|| ...) dà un errore diverso, E0373 («closure may outlive the current function»), perché il thread potrebbe durare più di main. Soluzione: thread::spawn(move || ...), condividere con Arc, oppure usare std::thread::scope. Vedi Concorrenza.
Un riferimento a un valore temporaneo. let label = Label { text: String::from("Inbox").as_str() }; è parente stretto di E0597, ma compare come E0716, «temporary value dropped while borrowed»: la String non viene mai salvata in una variabile, quindi viene eliminata alla fine dell’istruzione. Soluzione: mettila prima in una variabile (let title = String::from("Inbox");), oppure rendi il campo una String.
Restituire un riferimento da una funzione. Se il valore viene creato dentro la funzione, ottieni invece E0515. Restituisci il valore posseduto.
Errori correlati
Sezione intitolata “Errori correlati”- E0515: cannot return reference to local variable: lo stesso problema, ma attraverso il confine di una funzione.
- E0373: closure may outlive the current function: la versione con i thread di questo errore. Di solito si risolve con
move. - E0106: missing lifetime specifier: Rust deve sapere da cosa prende in prestito un riferimento restituito o salvato.
- E0382: borrow of moved value: la trappola opposta, usare un valore dopo averlo ceduto.
- E0502: mutable borrow while immutably borrowed: un riferimento è ancora in uso quando provi a cambiare il valore.
E0716: temporary value dropped while borrowed: come E0597, ma per un valore che non è mai stato salvato in una variabile.- Tutti gli errori del compilatore spiegati
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. In let r; { let x = 5; r = &x; } seguito da un println! di r, cosa provoca davvero E0597?
2. A ogni giro del ciclo costruisci una String e inserisci &name in un Vec<&str>. Qual è la soluzione abituale?
3. La closure di un thread prende in prestito un Vec locale. Quale errore ottieni?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0597 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
- The Borrow Checker — The Rust Book (in inglese): lo stesso esempio con
rex, con un diagramma dei due lifetime. - Borrowing — Rust by Example (in inglese): brevi esempi eseguibili di prestiti e di quanto durano.
- Lifetimes — Rust by Example (in inglese): come il compilatore vede la vita di ogni valore e di ogni prestito.
- E0716 — Rust error codes index (in inglese): l’errore strettamente collegato «temporary value dropped while borrowed».