Salta ai contenuti

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

error[E0597]: `x` does not live long enough
--> src/main.rs:5:13
|
4 | let x = 5;
| - binding `x` declared here
5 | r = &x; // r borrows x
| ^^ borrowed value does not live long enough
6 | } // x is dropped here
| - `x` dropped here while still borrowed
7 | println!("r: {r}"); // ❌ r would point at nothing
| - borrow later used here

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

Le quattro etichette raccontano la storia in ordine:

  1. Riga 4: x viene creato dentro il blocco interno { ... }.
  2. Riga 5: r prende in prestito x. Fin qui tutto bene.
  3. Riga 6: il blocco finisce, quindi x viene eliminato (drop). Il prestito però è ancora vivo.
  4. Riga 7: r viene 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.

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.

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: 95
▶ Provalo tu

Regola 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"]
▶ Provalo tu

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.

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 arrivo
Etichetta: Posta in arrivo
▶ Provalo tu

Se 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 granchio
▶ Provalo tu

Se 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();.

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.

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