Rust-Fehler E0282: type annotations needed (Typangabe nötig)
E0282 bedeutet: Rust hat versucht, einen Typ selbst herauszufinden, und nicht genug Hinweise gefunden. Mit deiner Logik ist alles in Ordnung. Der Compiler braucht nur von dir den Namen eines Typs, meist mit ein paar zusätzlichen Zeichen. Diese Seite zeigt dir zuerst die Lösung und erklärt dann, wie Rusts Typ-Raten (die Typinferenz) funktioniert. So weißt du, wann du nachhelfen musst. Die anderen Fehler dieser Reihe findest du unter Rust-Compilerfehler.
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0282]: type annotations needed for `Vec<_>` --> src/main.rs:2:9 |2 | let mut scores = Vec::new(); // a Vec of... what? | ^^^^^^^^^^ ---------- type must be known at this point |help: consider giving `scores` an explicit type, where the type for type parameter `T` is specified |2 | let mut scores: Vec<T> = Vec::new(); // a Vec of... what? | ++++++++Dieses Programm erzeugt den Fehler:
fn main() { let mut scores = Vec::new(); // a Vec of... what? println!("Scores recorded: {}", scores.len());}Der Kommentar fragt: ein Vec von … was?
Gleiche Meldung, anderer Code
Abschnitt betitelt „Gleiche Meldung, anderer Code“Die Worte „type annotations needed“ (Typannotationen nötig) tauchen auch bei drei anderen Codes auf. Sie bedeuten dasselbe, und die Lösungen auf dieser Seite funktionieren für alle:
- E0283:
.collect()oder.sum()ohne Zieltyp. rustc ergänzt eine Notiz wiethe type must implement `FromIterator<&str>`oder`Sum<&i32>`, weil viele Typen als Ergebnis in Frage kommen. - E0284:
"42".parse().unwrap()ohne Zieltyp. Die Notiz lautetcannot satisfy `<_ as FromStr>::Err == _`. Das ist eine furchteinflößende Art zu fragen: „In was soll ich das parsen?“ - E0790:
let settings = Default::default();. Die Überschrift lautetcannot call associated function on trait without specifying the corresponding `impl` type: Rust weiß nicht, wessen Standardwert du willst.
Welchen Code du bekommst, hängt von der genauen Zeile ab, und er kann sich ändern, wenn du mehr Code hinzufügst. let age = "42".parse().unwrap(); ergibt zum Beispiel E0284. Ruft die nächste Zeile aber age.pow(2) auf, wird daraus E0282. Mach dir um die Nummer keine Sorgen. Schau auf die Variable, auf die rustc zeigt.
Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Schau dir die Markierungen unter Zeile 2 an:
^^^^^^^^^^untermut scores: Das ist die Variable, deren Typ Rust nicht herausfinden konnte.----------unterVec::new(): „type must be known at this point“, also „der Typ muss an dieser Stelle bekannt sein“.Vec::new()erzeugt eine leere Liste, aber eine leere Liste von was? Zahlen? Namen? Rust muss es wissen, um die richtige Art von Speicher vorzusehen.- Die Hilfe schlägt
let mut scores: Vec<T>vor.Tist hier nur ein Platzhalter. Du ersetzt ihn durch einen echten Typ, etwaVec<u32>.
Stell dir ein Paket bei der Post vor, bei dem das Feld „Inhalt“ leer geblieben ist. Der Mitarbeiter kann es nicht verschicken, bis jemand etwas hineinschreibt. Du kannst es selbst ausfüllen – oder etwas ins Paket legen, damit er sieht, was drin ist.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Jeder Wert in Rust hat genau einen Typ, und der steht beim Kompilieren fest. Meistens schreibst du ihn nicht hin, weil Rust deinen Code liest und ihn selbst herausfindet. let count = 5; ist ein i32. let name = String::from("Ferris"); ist ein String. Das ist Typinferenz, und die Lektion Variablen zeigt mehr davon.
Die Inferenz nutzt Hinweise aus der ganzen Funktion, nicht nur aus einer Zeile. Rufst du später scores.push(90) auf, ist das ein Hinweis: Der Vec enthält Zahlen. Übergibst du scores an eine Funktion, die &[u32] nimmt, ist das ein noch besserer Hinweis. E0282 tritt auf, wenn es gar keine Hinweise gibt, oder wenn Rust den Typ braucht, bevor es beim Hinweis ankommt (zum Beispiel, um eine Methode nachzuschlagen).
Manche Funktionen sind in ihrem Rückgabetyp generisch: parse, collect, sum, Default::default, Vec::new, None. Sie können viele verschiedene Typen liefern, also kann Rust keinen für dich aussuchen. Es rät nicht, denn ein falscher Tipp würde das Verhalten deines Programms stillschweigend ändern. Unter Generics siehst du, wie eine Funktion viele Typen zurückgeben kann.
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“Das ist in den meisten Fällen die richtige Wahl. Schreib : Typ hinter den Variablennamen. Das ist kurz und dokumentiert deinen Code gleich für die nächste Person, die ihn liest:
fn main() { let scores: Vec<u32> = Vec::new(); let age: u32 = "42".parse().unwrap(); let words: Vec<&str> = "rot grün blau".split(' ').collect(); let winner: Option<String> = None;
println!("Erfasste Punktzahlen: {}", scores.len()); println!("Nächstes Jahr wirst du {}", age + 1); println!("{:?}", words); println!("{:?}", winner);}Ausgabe:
Erfasste Punktzahlen: 0Nächstes Jahr wirst du 43["rot", "grün", "blau"]NoneDu musst nicht immer den vollständigen Typ schreiben. let words: Vec<_> = ...collect(); heißt: „ein Vec, und was drinsteckt, findest du selbst heraus“. Das _ bittet Rust, diesen Teil auszufüllen. Mehr zum Hinschreiben von Typen unter Datentypen.
Lösung 2: Den Turbofish ::<> benutzen
Abschnitt betitelt „Lösung 2: Den Turbofish ::<> benutzen“Nimm das, wenn es keine Variable zum Annotieren gibt, zum Beispiel mitten in einer Kette von Iterator-Aufrufen. Du schreibst den Typ mit ::<Typ> direkt an den Funktionsaufruf. ::<> heißt „Turbofish“, weil es ein bisschen wie ein Fisch aussieht:
fn main() { let input = "4 15 7"; let numbers = input .split(' ') .map(|s| s.parse::<u32>().unwrap()) .collect::<Vec<_>>(); let total = numbers.iter().sum::<u32>();
println!("{:?}", numbers); println!("Gesamt: {total}");}Ausgabe:
[4, 15, 7]Gesamt: 26Die drei häufigsten Turbofish-Aufrufe sind parse::<i32>(), collect::<Vec<_>>() und sum::<i32>(). Lösung 1 und Lösung 2 sind gleich richtig; let total: u32 = numbers.iter().sum(); erledigt dasselbe. Nimm, was sich besser liest.
Lösung 3: Den Wert benutzen, damit Rust den Typ ableiten kann
Abschnitt betitelt „Lösung 3: Den Wert benutzen, damit Rust den Typ ableiten kann“Nimm das, wenn der Fehler auftaucht, weil dein Code noch nicht fertig ist. Oft legst du einen leeren Vec an, und der Code, der ihn füllt, kommt erst später. Sobald du den Wert benutzt, hat Rust seinen Hinweis, und der Fehler verschwindet – ganz ohne Annotation:
fn print_team(names: &[String]) { for name in names { println!("- {name}"); }}
fn main() { let mut team = Vec::new(); // Typ unbekannt ... noch team.push(String::from("Ferris")); // jetzt weiß Rust: Vec<String> team.push(String::from("Corro")); print_team(&team);}Ausgabe:
- Ferris- CorroDie Reihenfolge spielt ein wenig mit. Der Hinweis muss kommen, bevor Rust den Typ braucht, um eine Methode aufzurufen. Ein Closure (eine anonyme Funktion) wie let shout = |s| s.to_uppercase(); scheitert trotzdem, selbst wenn du in der nächsten Zeile shout("hi") aufrufst. Rust muss nämlich wissen, was s ist, um .to_uppercase() zu finden. Dort schreibst du den Typ hin: |s: &str| s.to_uppercase(). Siehe Closures.
Lösung 4: Den Typ für None und Default::default() nennen
Abschnitt betitelt „Lösung 4: Den Typ für None und Default::default() nennen“Nimm das für Werte, die überhaupt keinen Hinweis mitbringen. None verrät nicht, was es enthalten würde, und Default::default() verrät nicht, wessen Standardwert du willst. Schreib den Typ in den Ausdruck:
#[derive(Debug, Default)]struct Settings { volume: u8, muted: bool,}
fn main() { let fastest_lap = None::<f64>; // statt: None let settings = Settings::default(); // statt: Default::default() let backup: Settings = Default::default(); // eine Annotation geht auch
println!("{:?}", fastest_lap); println!("{:?}", settings); println!("Lautstärke im Backup: {}", backup.volume);}Ausgabe:
NoneSettings { volume: 0, muted: false }Lautstärke im Backup: 0Settings::default() ist die klarste Wahl: Es liest sich als „die Standard-Settings“. None::<f64> ist praktisch, aber let fastest_lap: Option<f64> = None; (Lösung 1) ist üblicher. Siehe Option.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Text in eine Zahl parsen. let guess = line.trim().parse().unwrap(); ergibt E0284 (oder E0282, wenn du als Nächstes eine Methode auf guess aufrufst). parse kann ein i32, ein u8, ein f64 und vieles mehr liefern. Lösung: let guess: i32 = ... oder .parse::<i32>(). Später guess mit einer i32-Variable zu vergleichen, reicht hier nicht, denn parse braucht seinen Typ direkt beim Aufruf.
.collect(). Es kann einen Vec, einen String, ein HashSet, eine HashMap … bauen, deshalb muss es immer das Ziel kennen. Lösung: let words: Vec<_> = ...collect(); oder .collect::<Vec<_>>(). Siehe Iteratoren.
.sum() und .product(). nums.iter().sum() ergibt E0283 mit the type must implement `Sum<&i32>`. Die Summe von i32-Werten könnte ein i32 sein, aber auch andere Zahlentypen können Summen sein. Lösung: let total: i32 = nums.iter().sum(); oder .sum::<i32>().
Eine leere Collection in einem Struct-Konstruktor. let cache = HashMap::new(); in einer new()-Funktion, die du noch nicht fertig geschrieben hast. Lösung: Schreib den vollständigen Typ einmal hin (HashMap<String, u32>), oder gib den Wert in einem Struct zurück, dessen Feldtyp schon deklariert ist. Das ist ein Hinweis, den Rust nutzen kann.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0308: mismatched types. Das passiert, wenn deine Annotation und der Wert nicht zusammenpassen, etwa bei
let age: u32 = "42";. - E0277: trait bound not satisfied. Du hast einen Typ angegeben, aber er lässt sich so nicht bauen, etwa bei
let n: Vec<i32> = "1 2".split(' ').collect();(das ist einVec<&str>; parse zuerst jedes Element). - E0599: no method found, zum Beispiel wenn du
let n: u32annotierst und dann eineString-Methode wie.trim()darauf aufrufst. - E0004: non-exhaustive patterns, ein weiterer Fehler, bei dem rustc die Lösung für dich hinschreibt.
- Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Welche Zeile behebt E0282 bei let ids = Vec::new();, wenn der Vec u64-Werte enthalten soll?
2. Warum braucht let n = "7".parse().unwrap(); einen Typ, obwohl der Text eindeutig eine Zahl ist?
3. Was bedeutet das _ in .collect::<Vec<_>>()?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0282 – Index der Rust-Fehlercodes (auf Englisch): die offizielle Erklärung dieses Fehlers, mit weiteren Beispielen.
- E0283 – Index der Rust-Fehlercodes (auf Englisch): die Variante, die du bei
collectundsumsiehst, wenn viele Typen passen würden. - Datentypen – Die Programmiersprache Rust: erklärt Annotationen, einschließlich des klassischen Beispiels mit
"42".parse(). - Inference – Rust by Example (auf Englisch): ein kurzes Beispiel, wie Rust den Typ eines
Vecaus späterem Code ableitet. Iterator::collect– Doku der Standardbibliothek (auf Englisch): zeigt sowohl den Annotations-Stil als auch den Turbofish-Stil.