Rust-Fehler E0716: temporary value dropped while borrowed (temporärer Wert)
E0716 bedeutet: Du hast dir etwas von einem Wert ohne Variablennamen ausgeliehen und diesen Borrow dann in einer späteren Zeile weiter benutzt. Solche namenlosen Werte wirft Rust am Ende der Anweisung weg, also würde der Borrow ins Leere zeigen. Das sieht beängstigend aus, ist aber einer der am leichtesten zu behebenden Borrow-Fehler: Meistens teilst du einfach eine Zeile in zwei auf. (Diese Seite gehört zu Rust-Compilerfehler erklärt.)
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0716]: temporary value dropped while borrowed --> src/main.rs:2:13 |2 | let s = String::from("hi").as_str(); // s leiht sich einen String ohne Namen aus | ^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | creates a temporary value which is freed while still in use3 | println!("{s}"); // ❌ dieser String ist schon weg | - borrow later used here |help: consider using a `let` binding to create a longer lived value |2 ~ let binding = String::from("hi");3 ~ let s = binding.as_str(); // s leiht sich einen String ohne Namen aus |Auf Deutsch heißt das: „Ein temporärer Wert wurde gedroppt (aufgeräumt), während er noch ausgeliehen war.“ Dieses Programm erzeugt die Meldung:
fn main() { let s = String::from("hi").as_str(); // s leiht sich einen String ohne Namen aus println!("{s}"); // ❌ dieser String ist schon weg}Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Ein temporärer Wert (temporary) ist ein Wert, der mitten in einem Ausdruck entsteht, aber nie in einer Variablen gespeichert wird. In der Zeile oben baut String::from("hi") einen echten String, aber niemand besitzt ihn: Es gibt kein let name = ... dafür. Rust droppt einen temporären Wert am Ende der Anweisung, also beim ;.
Lies die drei Markierungen von links nach rechts:
^^^^unterString::from("hi"): Hier entsteht ein temporärerString(creates a temporary value).-am Ende der Zeile: Hier, beim;, wird dieserStringfreigegeben (freed at the end of this statement).-untersin Zeile 3: Abers(ein&str, das in diesenStringzeigt) wird hier noch benutzt (borrow later used here).
Stell dir einen Kellner vor, der deine Bestellung auf einen Klebezettel schreibt und den Zettel wegwirft, sobald er weggeht. Sagst du einer Freundin „die Bestellung steht auf dem Zettel“, findet sie nichts. Du musst den Zettel selbst behalten oder die Bestellung irgendwo aufschreiben, wo sie bleibt.
Der Teil help: der Meldung ist meistens schon die Antwort: Leg eine Variable an (rustc nennt sie binding), damit der Wert einen Besitzer hat, der länger lebt.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Das ist dieselbe Regel wie bei E0597: Eine Referenz darf nie länger leben als der Wert, auf den sie zeigt. Sonst wird sie zu einer hängenden Referenz (dangling reference) auf freigegebenen Speicher. Der einzige Unterschied: Bei E0597 hat der Wert einen Namen und stirbt an einer }. Bei E0716 hat der Wert keinen Namen und stirbt am ;.
Einen temporären Wert in derselben Anweisung zu benutzen, ist völlig in Ordnung. println!("{}", String::from("hi").as_str()); kompiliert, weil der Borrow vor dem ; endet. Der Fehler tritt nur auf, wenn der Borrow in einer späteren Zeile noch gebraucht wird. Siehe Wann endet ein Borrow? und Lifetimes.
Es gibt eine hilfreiche Ausnahme, die Verlängerung der Lebensdauer von Temporären (temporary lifetime extension). Schreibst du & (oder &mut) direkt vor den temporären Wert in einem let, hält Rust den Wert stillschweigend so lange am Leben wie die Variable. Deshalb kompilieren diese Zeilen:
struct Label<'a> { text: &'a str,}
fn make_title() -> String { String::from("Posteingang")}
fn main() { let greeting = &String::from("hi"); // ✅ verlängert: & direkt im let let queue = &mut vec![1, 2, 3]; // ✅ verlängert queue.push(4); let label = Label { text: &make_title() }; // ✅ verlängert: & direkt in einem Struct-Literal println!("{greeting} {queue:?} {}", label.text);}Ausgabe:
hi [1, 2, 3, 4] PosteingangDie Verlängerung wirkt nicht durch einen Methoden- oder Funktionsaufruf hindurch. String::from("hi").as_str(), make_title().as_str(), vec![1, 2].iter() und shout(&make_title()) leihen sich den temporären Wert alle innerhalb eines Aufrufs aus. Der Wert stirbt also trotzdem beim ;, und du bekommst E0716. Du musst dir nicht jeden Fall merken: Siehst du E0716, gib dem Wert einen Namen.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Dem temporären Wert mit let einen Namen geben
Abschnitt betitelt „Lösung 1: Dem temporären Wert mit let einen Namen geben“Nimm diese Lösung fast immer. Der Compiler schlägt sie vor, und bei den meisten E0716-Fehlern ist sie die richtige Antwort. Speichere den Wert zuerst in einer eigenen Variablen und leih dir in der nächsten Zeile etwas von dieser Variablen aus.
Ein sehr häufiger Fall ist das Zerlegen einer Eingabezeile. read_command() gibt einen neuen String zurück, .trim() und .split(' ') leihen sich etwas davon aus, und der Vec<&str> behält diese Borrows:
let parts: Vec<&str> = read_command().trim().split(' ').collect(); // ❌ E0716Rust zeigt auf read_command() und sagt, dass der Wert am Ende der Anweisung freigegeben wird. Gib ihm einen Namen:
fn read_command() -> String { // In einem echten Programm würde das eine Zeile von stdin lesen. String::from(" add milk eggs \n")}
fn main() { let line = read_command(); // der String lebt jetzt bis zum Ende von main let parts: Vec<&str> = line.trim().split(' ').collect(); println!("{parts:?}"); println!("Befehl: {}, Einträge: {}", parts[0], parts.len() - 1);}Ausgabe:
["add", "milk", "eggs"]Befehl: add, Einträge: 2Die Eingabe add milk eggs bleibt auf Englisch, weil sie wie ein Befehl in einem Programm aussieht – so, wie ein Nutzer ihn eintippen würde.
Genau das passiert dir auch mit echter Eingabe: lines.next().unwrap().unwrap().trim().split(' ').collect() auf io::stdin().lines() ergibt aus demselben Grund E0716. Schreib zuerst let line = lines.next().unwrap().unwrap(); in eine eigene Zeile. Siehe Strings.
Lösung 2: Eigene Werte statt Referenzen behalten
Abschnitt betitelt „Lösung 2: Eigene Werte statt Referenzen behalten“Nimm diese Lösung, wenn der Borrow länger leben muss als jede Variable, die du anlegen könntest – zum Beispiel in einem Struct, das du weiterreichst, oder in einem Vec, den du in einer Schleife füllst. Ein let innerhalb einer Schleife würde am Ende jedes Durchlaufs gedroppt. Damit würde aus E0716 nur E0597.
Hier sind zwei kaputte Versionen. Ein Struct, das ein &str aus einem temporären Wert hält, und eine Schleife, die Borrows neuer Strings einfügt:
let label = Label { text: make_title().as_str() }; // ❌ E0716
let mut names: Vec<&str> = Vec::new();for name in ["ferris", "corro"] { names.push(name.to_uppercase().as_str()); // ❌ E0716}Die Lösung: Lass das Struct und den Vec ihre Strings besitzen. Nimm String statt &str und lass das .as_str() weg:
struct Label { text: String, // eigener String, also keine Lifetime nötig}
fn make_title() -> String { String::from("Posteingang")}
fn main() { let label = Label { text: make_title() }; // der String wird in das Label verschoben println!("Label: {}", label.text);
let mut names: Vec<String> = Vec::new(); for name in ["ferris", "corro"] { names.push(name.to_uppercase()); // der Vec besitzt jeden neuen String } println!("{names:?}");}Ausgabe:
Label: Posteingang["FERRIS", "CORRO"]Für Einsteiger sind eigene Felder (String, Vec<T>) fast immer die einfachste Wahl. Ein Struct mit einem &str-Feld braucht eine Lifetime und kann nie länger leben als die Daten, die es ausleiht. Siehe Structs, die Referenzen enthalten und Structs.
Lösung 3: Die Kette umbauen
Abschnitt betitelt „Lösung 3: Die Kette umbauen“Nimm diese Lösung, wenn der temporäre Wert der Anfang einer Iterator-Kette ist. Diese Zeilen erzeugen einen Vec mit vec![...], leihen ihn mit .iter() aus und behalten den Iterator (oder sein Ergebnis) für später:
let doubled = &mut vec![1, 2, 3].iter().map(|n| n * 10); // ❌ E0716let best = vec![72, 95, 88].iter().max(); // ❌ E0716Das &mut davor hilft nicht: Die Verlängerung hält den Iterator am Leben, aber nicht den Vec, von dem .iter() ausleiht. Es gibt zwei Auswege:
- Nimm
into_iter(), damit der Iterator denVecbesitzt (das schlägt auch rustc vor). Dann brauchst du auch kein&mutmehr, nurlet mut. - Beende die Kette mit einem eigenen Ergebnis, etwa mit
.copied(),.cloned(),.sum()oder.collect::<Vec<_>>()aus einfachen Zahlen. Dann leiht nach dem;nichts mehr etwas vom temporären Wert aus.
fn main() { // Der Iterator besitzt den Vec, also leiht nichts von einem temporären Wert let mut doubled = vec![1, 2, 3].into_iter().map(|n| n * 10); println!("{:?}", doubled.next()); println!("{:?}", doubled.next());
// .copied() macht aus Option<&i32> ein Option<i32>, einen Wert ohne Borrow let best = vec![72, 95, 88].iter().max().copied(); println!("Bester: {best:?}");}Ausgabe:
Some(10)Some(20)Bester: Some(95)Brauchst du den Vec später noch einmal, geh zurück zu Lösung 1: let scores = vec![72, 95, 88]; und dann scores.iter().max(). Siehe Iteratoren.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Einen Mutex mitten in einer Kette sperren. let first = names.lock().unwrap().first(); ergibt E0716, weil der Lock-Guard ein temporärer Wert ist. Wird er gedroppt, wird die Sperre freigegeben – also darf first nicht weiter hineinzeigen. Lösung: let guard = names.lock().unwrap(); und dann guard.first(), oder du kopierst die Daten in einem Rutsch heraus, mit .first().cloned(). Siehe Shared State.
&temporärer_wert an eine Funktion übergeben, die eine Referenz zurückgibt. Mit fn shout(s: &str) -> &str ergibt die Zeile let t = shout(&make_title()); E0716: Das zurückgegebene &str leiht sich etwas vom temporären String aus, und die Verlängerung reicht nicht durch einen Funktionsaufruf hindurch. Lösung: let title = make_title(); und dann shout(&title).
Ihn aus einer Funktion zurückgeben. Schreibst du make_title().as_str() als letzte Zeile einer Funktion, die eine Referenz zurückgibt (etwa -> &'static str), bekommst du stattdessen E0515 („cannot return value referencing temporary value“, ein Wert, der auf einen temporären Wert verweist, kann nicht zurückgegeben werden). Lösung: Gib den String zurück.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0597: borrowed value does not live long enough (der ausgeliehene Wert lebt nicht lange genug): dasselbe Problem für einen Wert, der einen Namen hat und an einer
}gedroppt wird. - E0515: cannot return reference to local variable: einen Borrow eines lokalen oder temporären Werts aus einer Funktion zurückgeben.
- E0106: missing lifetime specifier (fehlende Lifetime-Angabe): Ein Struct-Feld wie
text: &strbraucht eine Lifetime. Oft ein Zeichen dafür, dassStringdie bessere Wahl ist. - E0502: mutable borrow while immutably borrowed: ein weiterer Fehler nach dem Muster „ein Borrow ist noch in Gebrauch“, diesmal beim Ändern statt beim Droppen von Daten.
- E0382: borrow of moved value (Borrow eines verschobenen Werts): Wenn du auf eigene Werte umgestiegen bist, pass auf, dass du einen Wert nicht benutzt, nachdem du ihn verschoben hast.
- Alle Compilerfehler erklärt
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Was ist in Rust ein „temporärer Wert“?
2. let s = String::from("hi").as_str(); gefolgt von println!("{s}"); ergibt E0716. Was ist die übliche Lösung?
3. Welche Zeile kompiliert dank der Verlängerung der Lebensdauer von Temporären?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0716 – Rust error codes index (auf Englisch): die offizielle Erklärung dieses Fehlers, mit der
let-Lösung. - Referenzen und Borrowing – Das Rust-Buch: die Regeln für Borrowing, auch dazu, warum Referenzen nie hängen können.
- Temporary scopes – The Rust Reference (auf Englisch): die genauen Regeln, wann ein temporärer Wert gedroppt wird.
- Temporary lifetime extension – The Rust Reference (auf Englisch): welche
let-Formen einen temporären Wert am Leben halten, mit Beispielen. - Referenzen validieren mit Lebensdauern – Das Rust-Buch: wie Rust vergleicht, wie lange ein Wert lebt und wie lange eine Referenz benutzt wird.