Rust-Fehler E0106: missing lifetime specifier (fehlende Lifetime)
E0106 bedeutet: Du hast eine Referenz (&) in den Rückgabetyp einer Funktion oder in ein Struct geschrieben, und Rust kann nicht erkennen, wovon sie sich etwas ausleiht. Das ist kein Fehler in deiner Logik. Der Compiler braucht nur noch ein Wort von dir – oder eine kleine Planänderung. Fast jeder trifft diesen Fehler in seiner ersten Rust-Woche, und die Lösung ist meistens eine einzige Zeile. (Diese Seite gehört zu Rust-Compilerfehler einfach erklärt.)
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0106]: missing lifetime specifier --> src/main.rs:1:32 |1 | fn longer(a: &str, b: &str) -> &str { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`help: consider introducing a named lifetime parameter |1 | fn longer<'a>(a: &'a str, b: &'a str) -> &'a str { | ++++ ++ ++ ++Auf Deutsch heißt das ungefähr: „Es fehlt eine Lifetime-Angabe.“ Der help:-Teil ergänzt: Der Rückgabetyp enthält einen geliehenen Wert, aber die Signatur sagt nicht, ob er von a oder von b geliehen ist.
Dieses Programm erzeugt die Meldung:
fn longer(a: &str, b: &str) -> &str { if a.len() >= b.len() { a } else { b }}
fn main() { println!("{}", longer("apple", "fig"));}Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Eine Referenz ist eine Leihgabe: Sie zeigt auf Daten, die jemand anderem gehören. Immer wenn eine Funktion eine Referenz zurückgibt, muss Rust wissen, in wessen Daten sie zeigt. Nur so kann es prüfen, dass diese Daten noch leben, während der Aufrufer das Ergebnis benutzt.
Schau dir die drei Markierungen unter Zeile 1 an. Der Compiler unterstreicht a und b mit ---- und den Rückgabetyp mit ^. Er sagt damit: „Du gibst einen geliehenen &str zurück, und der könnte aus a oder aus b kommen. Aus welchem?“ Raten will er nicht.
Stell dir einen Bibliotheksausweis vor. Wenn dir ein Freund ein Buch gibt und sagt „das ist geliehen“, willst du wissen, von wem – damit du weißt, wann es zurück muss. Eine Lifetime (Lebensdauer) ist genau dieses Etikett an der Leihgabe.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Rust garantiert, dass eine Referenz nie länger lebt als die Daten, auf die sie zeigt (keine „hängenden Zeiger“). Um das bei einem Funktionsaufruf zu prüfen, schaut sich der Compiler nur die Signatur der Funktion an, nicht ihren Rumpf. Also muss die Signatur selbst sagen, woher die zurückgegebene Referenz stammt.
Meistens schreibst du überhaupt keine Lifetimes, weil Rust sie mit drei einfachen Elision-Regeln (Auslassungsregeln) selbst ergänzt:
- Jeder Referenz-Parameter bekommt seine eigene Lifetime.
- Gibt es genau einen Referenz-Parameter, leiht sich die zurückgegebene Referenz von ihm.
- Ist es eine Methode mit
&selfoder&mut self, leiht sich die zurückgegebene Referenz vonself.
E0106 taucht auf, wenn keine dieser Regeln eine Antwort liefert: bei zwei oder mehr Referenz-Parametern (und ohne self), bei null Referenz-Parametern oder bei einer Referenz in einem Struct. Die Lektion Lifetimes erklärt das ausführlich, und Borrowing erklärt Referenzen an sich.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Eine benannte Lifetime ('a) hinzufügen
Abschnitt betitelt „Lösung 1: Eine benannte Lifetime ('a) hinzufügen“Nimm diese Lösung, wenn die Funktion wirklich ein Stück von einer ihrer Eingaben zurückgibt. 'a ist nur ein Name. Steht er an den Eingaben und an der Ausgabe, heißt das: „Das Ergebnis lebt so lange wie diese Eingaben.“
// 'a: Der zurückgegebene &str leiht sich von a oder b, also lebt er so lange wie beide.fn longer<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() >= b.len() { a } else { b }}
fn main() { let first = String::from("apple"); let second = String::from("fig"); println!("Längeres Wort: {}", longer(&first, &second));}Ausgabe:
Längeres Wort: appleDie Wörter „apple“ und „fig“ bleiben hier auf Englisch, weil die Funktion ihre Länge in Bytes vergleicht.
Kommt das Ergebnis immer nur aus einem Parameter, schreib 'a nur an diesen Parameter: fn lookup<'a>(map: &'a HashMap<String, String>, key: &str) -> Option<&'a str>. Der Vorschlag des Compilers schreibt 'a überallhin. Das funktioniert, ist aber strenger als nötig.
Lösung 2: Stattdessen einen eigenen String zurückgeben
Abschnitt betitelt „Lösung 2: Stattdessen einen eigenen String zurückgeben“Nimm diese Lösung, wenn die Funktion neuen Text baut oder wenn der Aufrufer einfach nicht an eine Leihgabe gebunden sein soll. Ein eigener String gehört dem Aufrufer, also gibt es nichts zu verfolgen:
fn greeting(name: &str, lang: &str) -> String { if lang == "de" { format!("Hallo, {name}!") } else { format!("Hello, {name}!") }}
fn main() { println!("{}", greeting("Ana", "de")); println!("{}", greeting("Sam", "en"));}Ausgabe:
Hallo, Ana!Hello, Sam!Das kostet eine kleine Speicherreservierung. Für die meisten Programme ist das völlig in Ordnung. Du musst deswegen kein schlechtes Gewissen haben.
Lösung 3: Eigene Daten im Struct speichern
Abschnitt betitelt „Lösung 3: Eigene Daten im Struct speichern“Ein Struct-Feld wie title: &str führt zum selben Fehler:
error[E0106]: missing lifetime specifier --> src/main.rs:2:12 |2 | title: &str, | ^ expected named lifetime parameterBei Structs ist die beste Lösung fast immer, die Daten selbst zu besitzen: Nimm String statt &str und Vec<T> statt &[T]. Dann kann das Struct so lange leben, wie du willst:
#[derive(Debug)]struct Book { title: String, // eigene Daten, kein &str pages: u32,}
fn main() { let book = Book { title: String::from("Dune"), pages: 412 }; println!("{} ({} Seiten)", book.title, book.pages);}Ausgabe:
Dune (412 Seiten)Gib einem Struct nur dann eine Lifetime, wenn es eine kurzlebige Ansicht auf Daten ist, die woanders leben – zum Beispiel ein Parser, der sich einen großen Text anschaut. Dann schreibst du <'a> an das Struct und an das Feld:
struct Excerpt<'a> { text: &'a str, // leiht sich vom Roman unten}
fn main() { let novel = String::from("Nennt mich Ismael. Vor einigen Jahren..."); let first = novel.split('.').next().unwrap(); let excerpt = Excerpt { text: first }; println!("Auszug: {}", excerpt.text);}Ausgabe:
Auszug: Nennt mich IsmaelLösung 4: 'static für String-Literale verwenden
Abschnitt betitelt „Lösung 4: 'static für String-Literale verwenden“Hat eine Funktion überhaupt keine Referenz-Parameter, sagt die Meldung „there is no value for it to be borrowed from“ (es gibt keinen Wert, von dem geliehen werden könnte):
error[E0106]: missing lifetime specifier --> src/main.rs:1:22 |1 | fn default_name() -> &str { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed fromGibst du nur String-Literale wie "OK" zurück, schreib &'static str. Literale stecken fest in deinem Programm, also leben sie während des ganzen Programmlaufs:
fn status_text(code: u16) -> &'static str { match code { 200 => "OK", 404 => "Not Found", _ => "Unknown", }}
fn main() { println!("200 bedeutet {}", status_text(200)); println!("404 bedeutet {}", status_text(404));}Ausgabe:
200 bedeutet OK404 bedeutet Not FoundNimm 'static nur für echte Literale und Konstanten. Wird der Text erst zur Laufzeit gebaut (mit format! oder aus einer Datei gelesen), hilft 'static nicht. Gib dann einen String zurück (Lösung 2).
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“- Hilfsfunktionen mit zwei Eingaben, etwa
fn lookup(map: &HashMap<String, String>, key: &str) -> Option<&str>. Lösung: Binde die Ausgabe mit'anur anmap(Lösung 1). - Structs, die Text enthalten, etwa ein
User { name: &str }, das aus eingelesenen Daten gebaut wird. Lösung:String-Felder (Lösung 3). - Funktionen, die feste Meldungen zurückgeben, etwa
fn error_label() -> &str. Lösung:&'static str(Lösung 4).
Bei Methoden gibt es gute Nachrichten: fn name(&self) -> &str braucht dank Regel 3 überhaupt keine Lifetime.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0382: borrow of moved value: ein weiterer Ownership-Fehler – du benutzt einen Wert, nachdem du ihn weggegeben hast.
- E0502: mutable borrow while immutably borrowed: der Borrow Checker bei der Arbeit, sobald deine Lifetimes stehen.
- E0499: two mutable borrows: zwei
&mutauf dieselben Daten zur gleichen Zeit. - E0308: mismatched types: zum Beispiel, wenn du einen
Stringaus einer Funktion zurückgibst, die&strverspricht. E0515: cannot return reference to local variable: Du hast Lösung 1 bei Daten probiert, die in der Funktion selbst entstehen. Gib stattdessen einen eigenenStringzurück (Lösung 2).- Alle Compilerfehler erklärt
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Warum kompiliert fn first_word(s: &str) -> &str ganz ohne Lifetime?
2. Dein Struct Config { path: &str } führt zu E0106. Was ist meistens die beste Lösung?
3. Eine Funktion baut mit format! eine Meldung und gibt sie zurück. Welchen Rückgabetyp sollte sie haben?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0106 – Rust error codes index (auf Englisch): die offizielle Erklärung dieses Fehlers.
- Referenzen validieren mit Lebensdauern – Das Rust-Buch: die ganze Geschichte der Lifetimes, mit dem Beispiel
longestund Structs, die Referenzen enthalten. - Lifetime elision – The Rust Reference (auf Englisch): die genauen Regeln, nach denen Rust Lifetimes für dich ergänzt.
- Elision – Rust by Example (auf Englisch): kurze Beispiele dafür, wann du Lifetimes weglassen kannst.
- Lifetimes – Rust by Example (auf Englisch): lauffähige Beispiele für ausdrückliche Lifetimes in Funktionen, Methoden und Structs.