Errore Rust E0515: cannot return reference to local variable
E0515 significa: la tua funzione prova a restituire un riferimento (&) a qualcosa che ha creato lei stessa. Tutto quello che una funzione crea viene eliminato quando la funzione termina, quindi il riferimento punterebbe al nulla. Quasi tutti i principianti di Rust ci sbattono contro, e la soluzione di solito è togliere un solo & e restituire il valore stesso. (Questa pagina fa parte di Errori del compilatore Rust spiegati.)
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0515]: cannot return reference to local variable `greeting` --> src/main.rs:3:5 |3 | &greeting // ❌ greeting is dropped when we return | ^^^^^^^^^ returns a reference to data owned by the current functionIl compilatore dice: «non puoi restituire un riferimento alla variabile locale greeting». Questo è il programma che lo produce:
fn make_greeting(name: &str) -> &str { let greeting = format!("Hello, {name}!"); // a new String, owned by this function &greeting // ❌ greeting is dropped when we return}
fn main() { let text = make_greeting("Ferris"); println!("{text}");}I commenti dicono: greeting è una nuova String posseduta da questa funzione, e viene eliminata quando la funzione termina. Vedrai lo stesso errore con formulazioni diverse, che vogliono dire tutte la stessa cosa:
cannot return reference to local variablequando scrivi&greeting.cannot return value referencing local variablequando chiami un metodo che prende in prestito, comegreeting.as_str()o&scores[2..].cannot return value referencing temporary valuequando i dati non sono mai stati salvati in una variabile, come intext.to_uppercase().as_str().
Tutte le versioni portano l’etichetta «returns a value referencing data owned by the current function» (restituisce un valore che fa riferimento a dati posseduti dalla funzione corrente).
Cosa significa
Sezione intitolata “Cosa significa”greeting è una variabile locale: viene creata dentro make_greeting e le appartiene. Quando la funzione termina, tutte le sue variabili locali vengono eliminate. Il &str che restituisci punterebbe dentro una String che non esiste più.
È come traslocare da un appartamento e dare la chiave a un amico. La chiave è vera, ma l’appartamento ora è vuoto, o ci abita qualcun altro. Al tuo amico servono i mobili, non la chiave.
Un modo comune di arrivare qui: prima hai scritto greeting (una String) in una funzione che restituisce &str, hai ottenuto E0308 (tipi non corrispondenti), e il compilatore ti ha suggerito «consider borrowing here: &greeting». Quel suggerimento sistema il tipo, ma ti porta dritto a E0515. La vera soluzione è cambiare il tipo di ritorno.
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. Restituire un riferimento a una variabile locale creerebbe un riferimento pendente (dangling reference), un puntatore a memoria già liberata. In C e C++ questo compila e poi va in crash (o peggio, restituisce in silenzio dati spazzatura). Rust lo blocca in fase di compilazione.
Un riferimento restituito deve prendere in prestito da qualcosa che vive fuori dalla funzione: un parametro passato da chi chiama, un campo di self, oppure un valore 'static come un letterale stringa. Le variabili locali della funzione non vanno bene. Ecco perché le annotazioni di lifetime non risolvono E0515: 'a descrive soltanto quanto vivono i dati, non può far vivere più a lungo una variabile locale. Vedi Ownership e Prestito (borrowing).
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: Restituisci il valore posseduto
Sezione intitolata “Soluzione 1: Restituisci il valore posseduto”Scegli questa quando la funzione crea i dati. Nella maggior parte dei casi è la risposta giusta. Restituisci String invece di &str (o Vec<T> invece di &[T]) e togli il &. Il valore viene spostato a chi chiama, che da quel momento lo possiede:
fn make_greeting(name: &str) -> String { let greeting = format!("Ciao, {name}!"); greeting // ✅ consegna l'intera String a chi chiama}
fn top_scores() -> Vec<u32> { let mut scores = vec![42, 97, 63, 88]; scores.sort(); scores.split_off(2) // i due più alti, in un nuovo Vec}
fn main() { let text = make_greeting("Ferris"); println!("{text}"); println!("{:?}", top_scores());}Output:
Ciao, Ferris![88, 97]Non preoccuparti del costo: restituire una String o un Vec sposta solo un piccolo puntatore, la lunghezza e la capacità. Il testo in sé non viene copiato. Potresti anche scrivere semplicemente format!("Ciao, {name}!") come ultima riga.
Soluzione 2: Prendi i dati come parametro e restituisci un riferimento al loro interno
Sezione intitolata “Soluzione 2: Prendi i dati come parametro e restituisci un riferimento al loro interno”Scegli questa quando chi chiama possiede già i dati e tu vuoi solo indicarne una parte. Se la funzione lavora su un parametro preso in prestito, il riferimento restituito prende in prestito da quel parametro, che vive fuori dalla funzione:
fn top_two(scores: &mut [u32]) -> &[u32] { scores.sort(); &scores[scores.len() - 2..] // ✅ punta dentro i dati di chi chiama}
fn main() { let mut scores = vec![42, 97, 63, 88]; // posseduto da main let best = top_two(&mut scores); println!("{best:?}");}Output:
[88, 97]Con un solo parametro riferimento, Rust completa il lifetime al posto tuo. Con due o più, potrebbe servirti 'a (vedi E0106). Funziona solo se restituisci un pezzo dell’input. Un valore nuovo costruito a partire dall’input (to_uppercase(), format!, trim().to_string()) è di nuovo una variabile locale, quindi torna alla Soluzione 1. Vedi Slice.
Soluzione 3: Restituisci &'static str per i letterali stringa
Sezione intitolata “Soluzione 3: Restituisci &'static str per i letterali stringa”Scegli questa quando ogni risultato possibile è un testo fisso scritto nel tuo codice. I letterali stringa sono salvati nel programma stesso e vivono per sempre, quindi restituire un riferimento a uno di loro è sempre sicuro:
fn status_text(code: u16) -> &'static str { match code { 200 => "OK", 404 => "Not Found", 500 => "Internal Server Error", _ => "Sconosciuto", }}
fn main() { for code in [200, 404, 418] { println!("{code}: {}", status_text(code)); }}Output:
200: OK404: Not Found418: SconosciutoI testi di stato HTTP restano in inglese, perché sono quelli standard. Non provare a «sistemare» il risultato di un format! scrivendo &'static str nella firma. Otterrai di nuovo E0515, perché la String resta locale.
Soluzione 4: Usa Cow quando a volte prendi in prestito e a volte crei
Sezione intitolata “Soluzione 4: Usa Cow quando a volte prendi in prestito e a volte crei”Scegli questa quando la funzione di solito restituisce l’input senza modifiche, ma a volte deve costruire una stringa nuova. Cow (clone-on-write, «copia in scrittura») contiene o un &str preso in prestito o una String posseduta, così allochi memoria solo quando serve:
use std::borrow::Cow;
fn safe_file_name(name: &str) -> Cow<'_, str> { if name.contains(' ') { Cow::Owned(name.replace(' ', "_")) // nuova String solo quando serve } else { Cow::Borrowed(name) // nessuna copia }}
fn main() { for name in ["relazione.txt", "i miei appunti.txt"] { let clean = safe_file_name(name); let kind = match clean { Cow::Borrowed(_) => "preso in prestito", Cow::Owned(_) => "posseduto", }; println!("{clean} ({kind})"); }}Output:
relazione.txt (preso in prestito)i_miei_appunti.txt (posseduto)Cow è un’ottimizzazione. Se non sei sicuro che ti serva, restituisci una String (Soluzione 1). Trovi di più in Weak e Cow.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Restituire s.as_str() di una String locale. greeting.as_str() prende in prestito greeting, quindi fallisce come &greeting, con la formulazione «cannot return value referencing local variable». Soluzione: restituisci greeting e usa String come tipo di ritorno.
Un .map() che crea stringhe nuove. Sembra innocuo, ma ogni to_lowercase() crea una String temporanea che viene eliminata subito dopo .as_str():
fn tags(input: &str) -> Vec<&str> { input.split(',').map(|t| t.trim().to_lowercase().as_str()).collect() // ❌ cannot return value referencing temporary value}Soluzione: restituisci Vec<String> e togli .as_str(): .map(|t| t.trim().to_lowercase()).collect(). (Senza to_lowercase(), t.trim() è una slice di input, quindi Vec<&str> funzionerebbe. Vedi Iteratori.)
Un metodo che formatta un campo. fn full_name(&self) -> &str { &format!("{} {}", self.first, self.last) } dà «cannot return reference to temporary value». Soluzione: -> String. Restituire &self.first invece va bene, perché quei dati vivono nella struct, non nel metodo. Vedi Struct.
Una funzione senza parametri riferimento. fn make_greeting() -> &String fallisce prima, con E0106, perché non c’è niente da cui possa prendere in prestito. La soluzione è la stessa: restituisci String.
Errori correlati
Sezione intitolata “Errori correlati”- E0106: missing lifetime specifier: spesso compare per primo; aggiungere
'anon risolve un riferimento a una variabile locale. - E0597: borrowed value does not live long enough: lo stesso problema dentro una sola funzione, con un blocco o un ciclo invece di un return.
- E0308: mismatched types: restituire
Stringdove è dichiarato&str. Cambia il tipo di ritorno, non aggiungere&. - E0382: borrow of moved value: dopo essere passato ai valori posseduti, fai attenzione a non usare un valore dopo averlo spostato.
E0716: temporary value dropped while borrowed: un riferimento a un valore temporaneo usato più avanti nella stessa funzione.- Tutti gli errori del compilatore spiegati
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Una funzione costruisce un messaggio con format! e restituisce &message. Di solito qual è la soluzione giusta?
2. Quale funzione compila?
3. Quando Cow<str> è un buon tipo di ritorno?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0515 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
- Dangling References — The Rust Book (in inglese): l’esempio
danglee perché la risposta è restituire direttamente laString. - Validating References with Lifetimes — The Rust Book (in inglese): perché un riferimento restituito deve essere legato a uno dei parametri.
- The Slice Type — The Rust Book (in inglese): restituire slice
&strche prendono in prestito dall’input, la versione sicura della Soluzione 2. Cow— standard library docs (in inglese): il tipo clone-on-write della Soluzione 4, con esempi.