Rust-Fehler E0515: cannot return reference to local variable (hängende Referenz)
E0515 bedeutet: Deine Funktion versucht, eine Referenz (&) auf etwas zurückzugeben, das sie selbst erzeugt hat. Alles, was eine Funktion erzeugt, wird aufgeräumt, wenn sie zurückkehrt. Die Referenz würde also ins Leere zeigen. Fast jeder Rust-Anfänger stößt auf diesen Fehler, und die Lösung ist meistens, ein einziges & zu entfernen und den Wert selbst zurückzugeben. (Diese Seite gehört zu Rust-Compilerfehler einfach erklärt.)
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0515]: cannot return reference to local variable `greeting` --> src/main.rs:3:5 |3 | &greeting // ❌ greeting wird beim Zurückkehren freigegeben | ^^^^^^^^^ returns a reference to data owned by the current functionAuf Deutsch heißt das ungefähr: „Eine Referenz auf die lokale Variable greeting kann nicht zurückgegeben werden – sie zeigt auf Daten, die der aktuellen Funktion gehören.“ Dieses Programm erzeugt die Meldung:
fn make_greeting(name: &str) -> &str { let greeting = format!("Hello, {name}!"); // ein neuer String, der dieser Funktion gehört &greeting // ❌ greeting wird beim Zurückkehren freigegeben}
fn main() { let text = make_greeting("Ferris"); println!("{text}");}Du begegnest diesem Fehler in ein paar Formulierungen. Sie bedeuten alle dasselbe:
cannot return reference to local variable, wenn du&greetingschreibst.cannot return value referencing local variable, wenn du eine Methode aufrufst, die ausleiht, etwagreeting.as_str()oder&scores[2..].cannot return value referencing temporary value, wenn die Daten nie in einer Variablen gelandet sind, etwa beitext.to_uppercase().as_str().
Jede Variante trägt das Label „returns a value referencing data owned by the current function“ (gibt einen Wert zurück, der auf Daten der aktuellen Funktion verweist).
Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“greeting ist eine lokale Variable: Sie entsteht in make_greeting und gehört dieser Funktion. Wenn die Funktion zurückkehrt, werden alle ihre lokalen Variablen freigegeben (gedroppt). Der zurückgegebene &str würde in einen String zeigen, den es nicht mehr gibt.
Das ist, als würdest du aus deiner Wohnung ausziehen und deinem Freund den Schlüssel geben. Der Schlüssel ist echt, aber die Wohnung ist leer – oder jemand anderes wohnt jetzt dort. Dein Freund braucht die Möbel, nicht den Schlüssel.
Ein typischer Weg hierher: Du hast zuerst greeting (einen String) in einer Funktion zurückgegeben, die &str verspricht, und E0308 (mismatched types) bekommen. Der Compiler hat „consider borrowing here: &greeting“ vorgeschlagen. Dieser Vorschlag repariert den Typ, führt aber direkt zu E0515. Die eigentliche Lösung ist, den Rückgabetyp zu ändern.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Eine Referenz darf nie länger leben als der Wert, auf den sie zeigt. Eine Referenz auf eine lokale Variable zurückzugeben, würde eine hängende Referenz (Dangling Reference) erzeugen, also einen Zeiger auf freigegebenen Speicher. In C und C++ kompiliert so etwas und stürzt dann ab (oder liefert, noch schlimmer, still Datenmüll). Rust fängt es schon beim Kompilieren ab.
Eine zurückgegebene Referenz muss sich von etwas leihen, das außerhalb der Funktion lebt: von einem Parameter, den der Aufrufer übergeben hat, von einem Feld von self oder von einem 'static-Wert wie einem String-Literal. Die lokalen Variablen der Funktion zählen nicht dazu. Deshalb können Lifetime-Annotationen E0515 nicht beheben: 'a beschreibt nur, wie lange Daten leben – es kann eine lokale Variable nicht länger leben lassen. Siehe Ownership und Borrowing.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Den eigenen Wert zurückgeben
Abschnitt betitelt „Lösung 1: Den eigenen Wert zurückgeben“Nimm diese Lösung, wenn die Funktion die Daten erzeugt. Das ist meistens die richtige Antwort. Gib String statt &str zurück (oder Vec<T> statt &[T]) und lass das & weg. Der Wert wird zum Aufrufer verschoben, und dem gehört er dann:
fn make_greeting(name: &str) -> String { let greeting = format!("Hallo, {name}!"); greeting // ✅ den ganzen String an den Aufrufer übergeben}
fn top_scores() -> Vec<u32> { let mut scores = vec![42, 97, 63, 88]; scores.sort(); scores.split_off(2) // die zwei höchsten, als neuer Vec}
fn main() { let text = make_greeting("Ferris"); println!("{text}"); println!("{:?}", top_scores());}Ausgabe:
Hallo, Ferris![88, 97]Keine Sorge wegen der Kosten: Wenn du einen String oder Vec zurückgibst, wird nur ein kleiner Zeiger samt Länge und Kapazität verschoben. Der Text selbst wird nicht kopiert. Du könntest als letzte Zeile auch einfach format!("Hallo, {name}!") schreiben.
Lösung 2: Die Daten als Parameter nehmen und eine Referenz hinein zurückgeben
Abschnitt betitelt „Lösung 2: Die Daten als Parameter nehmen und eine Referenz hinein zurückgeben“Nimm diese Lösung, wenn die Daten dem Aufrufer schon gehören und du nur auf einen Teil davon zeigen willst. Arbeitet die Funktion mit einem geliehenen Parameter, leiht sich die zurückgegebene Referenz von diesem Parameter – und der lebt außerhalb der Funktion:
fn top_two(scores: &mut [u32]) -> &[u32] { scores.sort(); &scores[scores.len() - 2..] // ✅ zeigt in die Daten des Aufrufers}
fn main() { let mut scores = vec![42, 97, 63, 88]; // gehört main let best = top_two(&mut scores); println!("{best:?}");}Ausgabe:
[88, 97]Bei einem einzigen Referenz-Parameter ergänzt Rust die Lifetime für dich. Bei zwei oder mehr brauchst du eventuell 'a (siehe E0106). Das klappt nur, wenn du ein Stück der Eingabe zurückgibst. Ein ganz neuer Wert, der aus der Eingabe gebaut wird (to_uppercase(), format!, trim().to_string()), ist wieder lokal – dann zurück zu Lösung 1. Siehe Slices.
Lösung 3: &'static str für String-Literale zurückgeben
Abschnitt betitelt „Lösung 3: &'static str für String-Literale zurückgeben“Nimm diese Lösung, wenn jedes mögliche Ergebnis ein fester Text in deinem Code ist. String-Literale liegen im Programm selbst und leben für immer. Eine Referenz darauf zurückzugeben, ist also immer sicher:
fn status_text(code: u16) -> &'static str { match code { 200 => "OK", 404 => "Not Found", 500 => "Internal Server Error", _ => "Unknown", }}
fn main() { for code in [200, 404, 418] { println!("{code}: {}", status_text(code)); }}Ausgabe:
200: OK404: Not Found418: UnknownDie Statustexte bleiben auf Englisch, weil HTTP sie genau so festlegt.
Versuch nicht, ein format!-Ergebnis zu „retten“, indem du &'static str in die Signatur schreibst. Du bekommst nur wieder E0515, denn der String ist immer noch lokal.
Lösung 4: Cow, wenn du mal ausleihst und mal neu erzeugst
Abschnitt betitelt „Lösung 4: Cow, wenn du mal ausleihst und mal neu erzeugst“Nimm diese Lösung, wenn die Funktion ihre Eingabe meistens unverändert zurückgibt, aber manchmal einen neuen String bauen muss. Cow (Clone-on-Write) enthält entweder einen geliehenen &str oder einen eigenen String. So reservierst du nur dann Speicher, wenn es nötig ist:
use std::borrow::Cow;
fn safe_file_name(name: &str) -> Cow<'_, str> { if name.contains(' ') { Cow::Owned(name.replace(' ', "_")) // neuer String nur, wenn nötig } else { Cow::Borrowed(name) // gar keine Kopie }}
fn main() { for name in ["bericht.txt", "meine notizen.txt"] { let clean = safe_file_name(name); let kind = match clean { Cow::Borrowed(_) => "geliehen", Cow::Owned(_) => "neu erzeugt", }; println!("{clean} ({kind})"); }}Ausgabe:
bericht.txt (geliehen)meine_notizen.txt (neu erzeugt)Cow ist eine Optimierung. Wenn du nicht sicher bist, ob du es brauchst, gib einen String zurück (Lösung 1). Mehr dazu unter Weak & Cow.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“s.as_str() eines lokalen String zurückgeben. greeting.as_str() leiht sich greeting. Es scheitert also genauso wie &greeting, nur mit der Formulierung „cannot return value referencing local variable“. Lösung: Gib greeting zurück und mach den Rückgabetyp zu String.
Ein .map(), das neue Strings erzeugt. Das sieht harmlos aus, aber jedes to_lowercase() erzeugt einen temporären String, der direkt nach .as_str() wieder freigegeben wird:
fn tags(input: &str) -> Vec<&str> { input.split(',').map(|t| t.trim().to_lowercase().as_str()).collect() // ❌ cannot return value referencing temporary value}Lösung: Gib Vec<String> zurück und lass .as_str() weg: .map(|t| t.trim().to_lowercase()).collect(). (Ohne to_lowercase() ist t.trim() ein Slice von input, dann würde Vec<&str> funktionieren. Siehe Iteratoren.)
Eine Methode, die ein Feld formatiert. fn full_name(&self) -> &str { &format!("{} {}", self.first, self.last) } führt zu „cannot return reference to temporary value“ (Referenz auf einen temporären Wert kann nicht zurückgegeben werden). Lösung: -> String. &self.first zurückzugeben ist dagegen in Ordnung, denn diese Daten leben im Struct, nicht in der Methode. Siehe Structs.
Eine Funktion ohne Referenz-Parameter. fn make_greeting() -> &String scheitert schon früher, mit E0106, weil es nichts gibt, wovon sie sich etwas leihen könnte. Die Lösung ist dieselbe: String zurückgeben.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0106: missing lifetime specifier: taucht oft zuerst auf; ein zusätzliches
'arepariert eine Referenz auf eine lokale Variable aber nicht. - E0597: borrowed value does not live long enough: dasselbe Problem innerhalb einer Funktion, mit einem Block oder einer Schleife statt einem
return. - E0308: mismatched types: Du gibst einen
Stringzurück, wo&strdeklariert ist. Ändere den Rückgabetyp, statt ein&hinzuzufügen. - E0382: borrow of moved value: Wenn du auf eigene Werte umgestiegen bist, pass auf, dass du einen Wert nicht mehr benutzt, nachdem du ihn verschoben hast.
E0716: temporary value dropped while borrowed: eine Referenz auf einen temporären Wert, die später in derselben Funktion benutzt wird.- Alle Compilerfehler erklärt
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Eine Funktion baut mit format! eine Meldung und gibt &message zurück. Was ist meistens die richtige Lösung?
2. Welche Funktion kompiliert?
3. Wann ist Cow<str> ein guter Rückgabetyp?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0515 – Rust error codes index (auf Englisch): die offizielle Erklärung dieses Fehlers.
- Hängende Referenzen – Das Rust-Buch: das Beispiel
dangleund warum die Antwort ist, denStringdirekt zurückzugeben. - Referenzen validieren mit Lebensdauern – Das Rust-Buch: warum eine zurückgegebene Referenz an einen der Parameter gebunden sein muss.
- Der Slice-Typ – Das Rust-Buch:
&str-Slices zurückgeben, die sich von der Eingabe leihen – die sichere Variante von Lösung 2. Cow– Doku der Standardbibliothek (auf Englisch): der Clone-on-Write-Typ aus Lösung 4, mit Beispielen.