Rust-Fehler E0284: type annotations needed (Typ für parse() fehlt)
E0284 bedeutet fast immer genau eins: Du hast .parse() aufgerufen, und Rust weiß nicht, in welchen Typ es den Text umwandeln soll. Die Notiz cannot satisfy `<_ as FromStr>::Err == _` sieht furchteinflößend aus, aber die Lösung sind meist nur ein paar Zeichen, etwa : u32 oder ::<u32>. Diese Seite zeigt dir zuerst die Lösung und entschlüsselt dann diese Notiz, damit sie ihren Schrecken verliert. Die anderen Fehler dieser Reihe findest du unter Rust-Compilerfehler.
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0284]: type annotations needed --> src/main.rs:2:9 |2 | let age = "42".parse().unwrap(); | ^^^ ----- type must be known at this point | = note: cannot satisfy `<_ as FromStr>::Err == _`help: consider giving `age` an explicit type |2 | let age: /* Type */ = "42".parse().unwrap(); | ++++++++++++„Type annotations needed“ heißt „Typangaben nötig“. Das help schlägt vor, age einen ausdrücklichen Typ zu geben.
Dieses Programm erzeugt den Fehler:
fn main() { let age = "42".parse().unwrap(); println!("Next year you'll be {}", age + 1);}Dieselbe Notiz siehst du mit .expect(...) statt .unwrap(), mit match "42".parse() { ... } und mit .parse() in einem Closure. Wird das Ergebnis von .parse() nicht in einer Variablen gespeichert, zeigt rustc direkt auf parse und schlägt parse::</* Type */>() vor.
Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Die Markierungen sind der einfache Teil:
^^^unterage: die Variable, deren Typ unbekannt ist.-----unterparse: „type must be known at this point“ (der Typ muss an dieser Stelle bekannt sein). Hier ist Rust stecken geblieben.- Das
help: „consider givingagean explicit type“. Ersetze/* Type */durch einen echten Typ, etwau32.
Jetzt die Notiz, Stück für Stück. In Compilermeldungen bedeutet _ „ein Typ, den ich noch nicht kenne“:
| Teil | Einfach gesagt |
|---|---|
<_ as FromStr> |
„der Typ, in den du umwandelst (ich kenne ihn noch nicht)“ |
::Err |
„… und der Fehlertyp, der dazugehört“ |
== _ |
„muss gleich einem anderen Typ sein, den ich auch nicht kenne“ |
Die ganze Notiz sagt also: „Ich kann nicht einmal herausfinden, welche Art Fehler parse zurückgeben könnte, weil ich nicht weiß, in was du umwandelst.“
Stell dir vor, du gibst einer Übersetzerin einen Brief und sagst nur: „Übersetz das.“ Ins Französische? Ins Japanische? Solange du es nicht sagst, kann sie nicht anfangen – und sie kann dir nicht einmal sagen, auf welche Probleme sie stoßen könnte.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“So sieht die Signatur von parse aus, leicht vereinfacht:
fn parse<F: FromStr>(&self) -> Result<F, F::Err>F ist der Typ, den du zurückhaben willst, und F::Err ist sein Fehlertyp. Jeder Zieltyp bringt seinen eigenen mit: Das Parsen eines i32 kann mit einem ParseIntError scheitern, ein f64 mit einem ParseFloatError, ein bool mit einem ParseBoolError. Rust braucht F, um überhaupt zu wissen, was parse zurückgibt.
Die Wahl ändert auch, was zur Laufzeit passiert. "42" lässt sich problemlos als u8, i32 oder f64 parsen. Aber "300" scheitert als u8 (zu groß), und "4.5" scheitert als i32 (keine ganze Zahl). Rust rät nicht, denn ein falscher Tipp würde zu einem Bug, der nur bei bestimmten Eingaben auftaucht. Die Lektion Konvertierungen zeigt, wie FromStr funktioniert und wie du es für deine eigenen Typen implementierst.
Wie Rust Typen selbst ableitet, erklärt die Seite zu E0282. Warum es drei verschiedene Codes (E0282, E0283, E0284) für „type annotations needed“ gibt, erklärt die Seite zu E0283. Kurz gesagt: E0284 ist die Variante, bei der die festhängende Regel einen Typ betrifft, der zu dem unbekannten Typ gehört (hier F::Err). Für dich ist die Lösung dieselbe: Nenn den Typ.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Die Variable annotieren
Abschnitt betitelt „Lösung 1: Die Variable annotieren“Nimm diese Lösung in den meisten Fällen. So macht es auch das Rust-Buch, und es dokumentiert gleich, was im Text stehen soll:
fn main() { let age: u32 = "42".parse().unwrap(); let price: f64 = "4.99".parse().unwrap(); let port: u16 = "8080".parse().expect("Port muss eine Zahl sein");
println!("Nächstes Jahr wirst du {}", age + 1); println!("Zwei Kaffee: {:.2}", price * 2.0); println!("Lausche auf Port {port}");}Ausgabe:
Nächstes Jahr wirst du 43Zwei Kaffee: 9.98Lausche auf Port 8080Nimm den Typ, der zur Bedeutung des Texts passt: u16 für einen Port, u8 für eine Prozentzahl, f64 für einen Preis. Ein zu kleiner Typ macht aus einer gültigen Eingabe einen Fehler. Nimm im Zweifel einen größeren Typ wie i64 oder u32.
Lösung 2: Den Turbofish parse::<T>() benutzen
Abschnitt betitelt „Lösung 2: Den Turbofish parse::<T>() benutzen“Nimm diese Lösung, wenn es keine Variable gibt, die du annotieren kannst: in einem Closure, mitten in einer Iterator-Kette oder in einem Ausdruck wie ... * 2:
fn main() { let line = "3 7 5"; let total = line .split(' ') .map(|s| s.parse::<u32>().unwrap()) .sum::<u32>(); let doubled = "21".parse::<i32>().unwrap() * 2;
println!("Summe: {total}"); println!("Verdoppelt: {doubled}");}Ausgabe:
Summe: 15Verdoppelt: 42Setz den Turbofish direkt an parse. In einer Kette reicht es oft nicht, nur das Endergebnis zu annotieren. Siehe den ersten Punkt unter „Wo du ihn oft siehst“ weiter unten.
Lösung 3: Den Typ nennen und den Fehler richtig behandeln
Abschnitt betitelt „Lösung 3: Den Typ nennen und den Fehler richtig behandeln“Nimm diese Lösung für echte Eingaben (Dateien, Kommandozeilenargumente, Webformulare), bei denen .unwrap() dein Programm bei falschem Text abstürzen ließe. Nenn den Typ an parse und mach ein match auf das Result. Sieh dir an, wie der Typ entscheidet, welche Eingaben klappen:
fn main() { for input in ["42", "300", "abc"] { match input.parse::<u8>() { Ok(n) => println!("{input}: ergibt {n}"), Err(e) => println!("{input}: {e}"), } }
let rate: Result<f64, _> = "0.25".parse(); println!("{:?}", rate);}Ausgabe:
42: ergibt 42300: number too large to fit in target typeabc: invalid digit found in stringOk(0.25)Die Fehlertexte kommen von Rust selbst und sind deshalb auf Englisch: „Zahl zu groß für den Zieltyp“ und „ungültige Ziffer im String“. In Result<f64, _> nennst du nur den Werttyp. Das _ lässt Rust den Fehlertyp (ParseFloatError) selbst einsetzen – genau der Teil, nach dem die Notiz gefragt hat. Mehr dazu unter Result.
Lösung 4: Den Wert dort benutzen, wo sein Typ feststeht
Abschnitt betitelt „Lösung 4: Den Wert dort benutzen, wo sein Typ feststeht“Nimm diese Lösung, wenn der geparste Wert in ein Struct-Feld oder einen Funktionsparameter mit bekanntem Typ geht. Rust liest die ganze Funktion, also beantwortet diese spätere Verwendung die Frage:
struct Config { port: u16, workers: u8,}
fn greet(age: u32) { println!("Willkommen! Du bist {age}.");}
fn main() { let port = "8080".parse().unwrap(); // u16, durch Config let workers = "4".parse().unwrap(); // u8, durch Config let config = Config { port, workers }; println!("Port {} mit {} Workern", config.port, config.workers);
let age = "42".parse().unwrap(); // u32, durch greet greet(age);}Ausgabe:
Port 8080 mit 4 WorkernWillkommen! Du bist 42.Nicht jede Verwendung zählt, deshalb ist das Lösung 4. Rechnungen wie age + 1 und Vergleiche wie age >= limit ergeben trotzdem E0284, selbst wenn limit ein u32 ist: Operatoren funktionieren zwischen vielen Typpaaren, sie legen also keinen genauen Typ fest. Und ein Methodenaufruf wie age.pow(2) macht aus dem Fehler E0282. Nimm im Zweifel Lösung 1.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Parsen in map, dann sum(). let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); ist immer noch E0284, obwohl total einen Typ hat. Ein u32 kann die Summe von u32-Werten oder von &u32-Werten sein, also verrät die Annotation Rust nicht, was jedes Element ist. Lösung: s.parse::<u32>(). (Sammelst du stattdessen in einen Vec<i32>, klappt es, weil ein Vec<i32> nur i32 enthalten kann.)
Parse-Ergebnisse in ein Result sammeln. let nums: Result<Vec<_>, _> = text.split(' ').map(|x| x.parse()).collect(); ist E0284: Das _ in Vec<_> lässt den Elementtyp offen. Lösung: Result<Vec<i32>, _>. Dieses praktische Muster hält beim ersten falschen Element an und gibt dir dessen Fehler.
Benutzereingaben lesen. let guess = line.trim().parse().unwrap(); in einem Ratespiel oder einem Kommandozeilenprogramm. Lösung: let guess: u32 = ..., genau wie im Rust-Buch.
Mit ? bekommst du einen seltsameren Fehler. let port = text.parse()?; ergibt kein E0284. In Edition 2021 weicht Rust auf den Typ () aus und meldet E0277: the trait bound `(): FromStr` is not satisfied (der Trait FromStr ist für () nicht implementiert; Edition 2024 sagt ! statt ()). Gleiche Lösung: let port: u16 = text.parse()?;.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0282: type annotations needed, wenn es gar keinen Hinweis gibt (oder der Typ für einen Methodenaufruf wie
age.pow(2)gebraucht wird). - E0283: type annotations needed nach
.sum(),.collect()oder.into(), wenn viele Typen passen. Dort steht auch, warum es drei verschiedene Codes sind. - E0790:
FromStr::from_str("42")oderDefault::default(), aufgerufen ohne zu sagen, welcher Typ gemeint ist. - E0277: was
.parse()?ohne Typ ergibt, und was du bekommst, wenn sich der gewählte Typ nicht aus Text parsen lässt. - E0599: keine Methode gefunden, zum Beispiel wenn du
.parse()auf etwas aufrufst, das kein Text ist, etwa eineu32-Variable (wandle sie stattdessen mitu8::try_from(n)um). - Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Was bedeutet cannot satisfy <_ as FromStr>::Err == _ einfach gesagt?
2. Welche Zeile behebt let port = "8080".parse().unwrap();?
3. let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); ergibt immer noch E0284. Wo gehört der Typ hin?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0284 – Rust error codes index (auf Englisch): die offizielle Erklärung dieses Fehlers.
str::parse– Doku der Standardbibliothek (auf Englisch): die Signatur der Methode, mit Annotation und Turbofish als Beispiel.FromStr– Doku der Standardbibliothek (auf Englisch): der Trait hinterparse, mit seinem TypErrund wie du ihn implementierst.- Ein Ratespiel programmieren – Das Rust-Buch: Hier taucht
let guess: u32 = guess.trim().parse()zum ersten Mal auf, Schritt für Schritt erklärt. - Datentypen – Das Rust-Buch: warum
"42".parse()eine Typangabe braucht, mit der echten Compilermeldung.