Zum Inhalt springen

Rust-Fehler E0597: borrowed value does not live long enough (lebt zu kurz)

E0597 bedeutet: Du hast eine Referenz (&x) länger behalten als den Wert x selbst. Der Wert wird am Ende seines Blocks aufgeräumt, aber etwas außerhalb dieses Blocks will die Referenz noch benutzen. Der Fehler ist sehr häufig, vor allem bei inneren Blöcken, Schleifen und Structs. Meistens behebst du ihn, indem du eine Zeile verschiebst oder einen eigenen Wert statt einer Referenz speicherst. (Diese Seite gehört zu Rust-Compilerfehler erklärt.)

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 leiht sich x aus
| ^^ borrowed value does not live long enough
6 | } // hier wird x aufgeräumt
| - `x` dropped here while still borrowed
7 | println!("r: {r}"); // ❌ r würde ins Leere zeigen
| - borrow later used here

Auf Deutsch heißt das: „x lebt nicht lange genug.“ Dieses Programm erzeugt die Meldung:

fn main() {
let r;
{
let x = 5;
r = &x; // r leiht sich x aus
} // hier wird x aufgeräumt
println!("r: {r}"); // ❌ r würde ins Leere zeigen
}

Die vier Markierungen erzählen die Geschichte der Reihe nach:

  1. Zeile 4: x wird innerhalb des inneren Blocks { ... } angelegt (binding x declared here).
  2. Zeile 5: r leiht sich x aus. So weit ist alles in Ordnung.
  3. Zeile 6: Der Block endet, also wird x gedroppt (aufgeräumt). Der Borrow lebt aber noch (dropped here while still borrowed).
  4. Zeile 7: r wird benutzt (borrow later used here), doch der Wert, auf den es zeigt, ist schon weg.

Stell dir vor, du notierst dir die Zimmernummer einer Freundin im Hotel. Das ist nützlich, solange sie dort wohnt. Sobald sie auscheckt, steht die Nummer zwar noch auf deinem Zettel, führt aber zu einem leeren Zimmer – oder zu einem fremden Gast. Rust lässt dich keinen Zettel behalten, der zu einem leeren Zimmer führt.

Du siehst auch die kurze Markierung „borrowed value does not live long enough“ (der ausgeliehene Wert lebt nicht lange genug). Das ist derselbe Fehler, nur nennt die Überschrift den Namen der Variablen.

Eine Referenz darf nie länger leben als der Wert, auf den sie zeigt. Sonst hättest du eine hängende Referenz (dangling reference): einen Zeiger auf Speicher, der schon freigegeben wurde und jetzt vielleicht etwas ganz anderes enthält. In C und C++ ist das eine klassische Ursache für Abstürze und Sicherheitslücken („use after free“).

Jeder Wert in Rust wird gedroppt, wenn sein Besitzer den Gültigkeitsbereich (Scope) verlässt – meist an der schließenden } des Blocks, in dem er deklariert wurde. Der Borrow Checker vergleicht zwei Zeitspannen: wie lange der Wert lebt und wie lange die Referenz benutzt wird. Wird die Referenz nach der } des Werts benutzt, bekommst du E0597. Siehe Gültigkeitsbereich (Scope), Borrowing und Lifetimes.

Beachte, worüber sich der Compiler nicht beschwert: das Ausleihen von x in Zeile 5. Das Problem ist nur die spätere Benutzung in Zeile 7. Lösch diese letzte Zeile, und das Programm kompiliert. Das ist ein guter Hinweis für die Lösungen unten: Entweder lässt du den Wert länger leben, oder du sorgst dafür, dass die Benutzung der Referenz früher endet.

Lösung 1: Den Wert in einen äußeren Scope verschieben

Abschnitt betitelt „Lösung 1: Den Wert in einen äußeren Scope verschieben“

Nimm diese Lösung, wenn der Wert wirklich so lange gebraucht wird wie die Referenz. Das ist die häufigste Lösung: Deklariere den Wert vor dem Block (außerhalb), damit er mindestens so lange lebt wie die Referenz.

fn main() {
let scores = vec![72, 95, 88]; // lebt jetzt bis zum Ende von main
let best;
{
best = scores.iter().max().unwrap();
}
println!("Bester Wert: {best}"); // ✅ scores lebt noch
}

Ausgabe:

Bester Wert: 95
▶ Selbst ausprobieren

Faustregel: Der Besitzer muss vor dem Ausleihenden deklariert werden und ihn überleben. Siehst du einen Wert, der in einem Block oder einer Schleife entsteht, und eine Referenz darauf, die außerhalb benutzt wird, dann zieh den Wert nach außen.

Lösung 2: Eigene Werte statt Referenzen speichern

Abschnitt betitelt „Lösung 2: Eigene Werte statt Referenzen speichern“

Nimm diese Lösung, wenn du Werte sammelst, die in einer Schleife entstehen. Jeder Durchlauf erzeugt einen neuen String und droppt ihn am Ende des Durchlaufs. Ein Vec<&str> außerhalb der Schleife kann also keine Referenzen darauf behalten:

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:?}");
}

Die Lösung: Lass den Vec die Strings besitzen. Ändere Vec<&str> zu Vec<String> und füge den String selbst ein. Dadurch wird er in den Vektor verschoben (moved) und lebt so lange wie der Vektor:

fn main() {
let mut names: Vec<String> = Vec::new();
for i in 1..=3 {
let name = format!("spieler{i}");
names.push(name); // den String in den Vec verschieben
}
println!("{names:?}");
}

Ausgabe:

["spieler1", "spieler2", "spieler3"]
▶ Selbst ausprobieren

Dieselbe Idee gilt für Structs und HashMaps: Entstehen die Daten in einem kleineren Scope als der Container, speichere String (oder Vec<T>) statt &str (oder &[T]). Für Einsteiger sind eigene Felder fast immer die richtige Wahl. Siehe Strings und Vektoren.

Nimm diese Lösung, wenn du das Original auch weiter benutzen musst oder nur einen Borrow davon hast. Ein Struct, das eine Referenz hält (Label<'a> { text: &'a str }), kann nicht länger leben als der String, auf den es zeigt. Gib dem Struct ein eigenes String-Feld und speichere eine Kopie darin:

struct Label {
text: String, // eigener String, kein &str
}
fn main() {
let label;
{
let title = String::from("Posteingang");
label = Label { text: title.clone() };
println!("Im Block: {title}");
}
println!("Label: {}", label.text); // ✅ label besitzt seine eigene Kopie
}

Ausgabe:

Im Block: Posteingang
Label: Posteingang
▶ Selbst ausprobieren

Brauchst du title danach nicht mehr, spar dir das Klonen und verschiebe den Wert: Label { text: title }. Einen kurzen String zu klonen ist billig und völlig in Ordnung. Hast du nur ein &str, bekommst du mit .to_string() eine eigene Kopie.

Lösung 4: Den Code so umbauen, dass der Borrow rechtzeitig endet

Abschnitt betitelt „Lösung 4: Den Code so umbauen, dass der Borrow rechtzeitig endet“

Nimm diese Lösung, wenn der Wert nur kurz leben soll. Statt den Wert länger leben zu lassen, benutzt du die Referenz, solange der Wert noch lebt. Das kommt oft bei Funktionen vor, die eine Referenz zurückgeben, wie beim klassischen Beispiel 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 the crab");
{
let second = String::from("Corro");
let winner = longest(&first, &second);
println!("Längster Name: {winner}"); // ✅ benutzt, bevor second gedroppt wird
}
}

Ausgabe:

Längster Name: Ferris the crab
▶ Selbst ausprobieren

Die beiden Namen bleiben auf Englisch, weil longest ihre Länge in Bytes vergleicht. Umlaute würden mehr Bytes belegen und könnten das Ergebnis verändern.

Deklarierst du winner außerhalb des Blocks und gibst es danach aus, bekommst du E0597 für second – obwohl first zufällig länger ist. Die Signatur sagt nämlich, dass das Ergebnis von jedem der beiden Argumente ausgeliehen sein kann. Also müssen beide länger leben als das Ergebnis. Brauchst du das Ergebnis wirklich später noch, speichere es als eigenen Wert: let winner = longest(&first, &second).to_string();.

Ein Closure, das außerhalb des Blocks gespeichert wird. Ein Closure, das name benutzt, leiht sich name aus. Lebt das Closure länger als name, bekommst du E0597 mit einer zusätzlichen Markierung, value captured here (hier wurde der Wert eingefangen):

let say_hello;
{
let name = String::from("Ferris");
say_hello = || println!("Hello, {name}!"); // ❌ `name` does not live long enough
}
say_hello();

Lösung: Schreib move || ..., damit das Closure name übernimmt (Ownership). Siehe Closures.

Threads. Leihst du dir in thread::spawn(|| ...) eine lokale Variable aus, bekommst du einen anderen Fehler, E0373 („closure may outlive the current function“, das Closure könnte die aktuelle Funktion überleben), weil der Thread länger laufen könnte als main. Lösung: thread::spawn(move || ...), Teilen mit Arc oder std::thread::scope. Siehe Nebenläufigkeit.

Eine Referenz auf einen temporären Wert. let label = Label { text: String::from("Inbox").as_str() }; ist nah an E0597, wird aber als E0716, „temporary value dropped while borrowed“ gemeldet (temporärer Wert wurde gedroppt, während er ausgeliehen war): Der String landet nie in einer Variablen und wird deshalb am Ende der Anweisung gedroppt. Lösung: Speichere ihn zuerst in einer Variablen (let title = String::from("Inbox");) oder mach das Feld zu einem String.

Eine Referenz aus einer Funktion zurückgeben. Entsteht der Wert innerhalb der Funktion, bekommst du stattdessen E0515. Gib den eigenen Wert zurück.

Kurzer Check

1. Was löst in let r; { let x = 5; r = &x; } mit einem anschließenden println! von r eigentlich E0597 aus?

2. Du baust in jedem Schleifendurchlauf einen String und fügst &name in einen Vec<&str> ein. Was ist die übliche Lösung?

3. Ein Thread-Closure leiht sich einen lokalen Vec aus. Welchen Fehler bekommst du?

Punkte: 0 / 3