Zum Inhalt springen

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.

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.

Die Markierungen sind der einfache Teil:

  1. ^^^ unter age: die Variable, deren Typ unbekannt ist.
  2. ----- unter parse: „type must be known at this point“ (der Typ muss an dieser Stelle bekannt sein). Hier ist Rust stecken geblieben.
  3. Das help: „consider giving age an explicit type“. Ersetze /* Type */ durch einen echten Typ, etwa u32.

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.

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.

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 43
Zwei Kaffee: 9.98
Lausche auf Port 8080
▶ Selbst ausprobieren

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

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: 15
Verdoppelt: 42
▶ Selbst ausprobieren

Setz 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 42
300: number too large to fit in target type
abc: invalid digit found in string
Ok(0.25)
▶ Selbst ausprobieren

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 Workern
Willkommen! Du bist 42.
▶ Selbst ausprobieren

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.

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()?;.

  • 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") oder Default::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 eine u32-Variable (wandle sie stattdessen mit u8::try_from(n) um).
  • Alle Compilerfehler: die vollständige Liste der Fehlerseiten.

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