Zum Inhalt springen

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

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 function

Auf 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 &greeting schreibst.
  • cannot return value referencing local variable, wenn du eine Methode aufrufst, die ausleiht, etwa greeting.as_str() oder &scores[2..].
  • cannot return value referencing temporary value, wenn die Daten nie in einer Variablen gelandet sind, etwa bei text.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).

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.

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.

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]
▶ Selbst ausprobieren

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]
▶ Selbst ausprobieren

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: OK
404: Not Found
418: Unknown
▶ Selbst ausprobieren

Die 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)
▶ Selbst ausprobieren

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.

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.

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