Zum Inhalt springen

Rust-Fehler E0107: wrong number of generic arguments (falsche Anzahl in < >)

E0107 bedeutet: Die Anzahl der Typen in <...> passt nicht zu dem, was der Typ oder die Funktion erwartet. Result will zwei (Result<T, E>), HashMap will zwei (HashMap<K, V>), Option und Vec wollen einen. Gibst du einen zu wenig oder einen zu viel an, sagt rustc etwas wie enum takes 2 generic arguments but 1 generic argument was supplied (das Enum nimmt 2 generische Argumente, aber 1 wurde angegeben), mit „struct“, „enum“, „type alias“, „function“ oder „method“ vorne. Es ist einer der freundlichsten Fehler: rustc nennt dir beide Zahlen, und die Lösung ist, einen Typ zu ergänzen oder zu streichen. Die Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.

error[E0107]: enum takes 2 generic arguments but 1 generic argument was supplied
--> src/main.rs:1:29
|
1 | fn parse_age(text: &str) -> Result<u32> { // ❌ `Result` braucht zwei Typen
| ^^^^^^ --- supplied 1 generic argument
| |
| expected 2 generic arguments
|
help: add missing generic argument
|
1 | fn parse_age(text: &str) -> Result<u32, E> { // ❌ `Result` braucht zwei Typen
| +++

Die Hilfe sagt: „Ergänze das fehlende generische Argument.“

Dieses Programm erzeugt den Fehler:

fn parse_age(text: &str) -> Result<u32> { // ❌ `Result` braucht zwei Typen
text.trim().parse()
}
fn main() {
for input in ["42", "forty"] {
match parse_age(input) {
Ok(age) => println!("age: {}", age),
Err(e) => println!("error: {}", e),
}
}
}

rustc zeigt zweimal auf Zeile 1 und gibt dazu eine Hilfe:

  1. Die ^^^^^^ unter Result: „expected 2 generic arguments“ (2 generische Argumente erwartet). Result ist ein Enum mit zwei Typparametern: T für den Erfolgswert und E für den Fehler.
  2. Die --- unter u32: „supplied 1 generic argument“ (1 generisches Argument angegeben). Du hast nur T angegeben.
  3. Das help: ergänzt einen Platzhalter E. Das ist ein Hinweis, wo der Typ hingehört, nicht der Typ selbst: Du ersetzt E durch den echten Fehlertyp.

Generische Argumente sind wie die Lücken auf einem Formular. Ein Result-Formular hat zwei Lücken, „wenn es klappt:“ und „wenn es schiefgeht:“. Füllst du nur eine aus, bekommst du es am Schalter zurück.

Dieser Fehler ist sehr häufig, wenn du Code gelesen hast, der Result<u32> mit nur einem Typ schreibt. Der Code war nicht falsch: Er hat einen Typalias wie io::Result<T> oder anyhow::Result<T> benutzt, der den Fehlertyp für dich einsetzt. Das normale Result aus dem Prelude hat keine solche Abkürzung.

Ein generischer Typ ist erst vollständig, wenn jeder Parameter einen Wert hat. Result<u32, E> mit unbekanntem E sagt weder, wie groß der Wert ist, noch, wie ein Fehler aussieht, also kann Rust Code damit nicht übersetzen. Rust könnte versuchen, den fehlenden Typ aus dem Funktionsrumpf zu erraten, leitet in einer Signatur aber absichtlich nie Typen ab. Signaturen sind der Vertrag, auf den sich anderer Code verlässt, darum müssen sie vollständig ausgeschrieben sein.

Manche Parameter haben Standardwerte. HashMap<K, V, S = RandomState> hat einen dritten Parameter für den Hasher. Deshalb sagt rustc bei einer HashMap „at least 2“ (mindestens 2). Standardwerte füllen nur von hinten auf, und nur dort, wo der Autor des Typs einen festgelegt hat. Siehe Generics und Result.

Das ist meistens die richtige Wahl. Finde heraus, was der fehlende Parameter sein soll, und schreib ihn hin. Bei parse() ist der Fehlertyp für eine Zahl, die sich nicht parsen lässt, std::num::ParseIntError:

use std::num::ParseIntError;
fn parse_age(text: &str) -> Result<u32, ParseIntError> {
text.trim().parse()
}
fn main() {
for input in ["42", "vierzig"] {
match parse_age(input) {
Ok(age) => println!("Alter: {}", age),
Err(e) => println!("Fehler: {}", e),
}
}
}

Ausgabe:

Alter: 42
Fehler: invalid digit found in string
▶ Selbst ausprobieren

Die Meldung „invalid digit found in string“ (ungültige Ziffer in der Zeichenkette) kommt von Rust selbst und bleibt darum Englisch. Du weißt nicht, welcher Fehlertyp es ist? Schau in die Dokumentation der Funktion, deren Fehler du zurückgibst. Dieselbe Lösung gilt für HashMap<String>: Sag, was die Werte sind, etwa HashMap<String, u32>. Siehe HashMap und Eigene Fehlertypen.

Lösung 2: Den Alias mit einem Typ benutzen, den du gemeint hast, etwa io::Result

Abschnitt betitelt „Lösung 2: Den Alias mit einem Typ benutzen, den du gemeint hast, etwa io::Result“

Nimm diese Lösung, wenn du mit Dateien, dem Netzwerk oder stdin arbeitest, oder wenn du Result<T> in fremdem Code gesehen hast. std::io::Result<T> ist die Kurzform von Result<T, std::io::Error>. Importiere das Modul und schreib io::Result:

use std::fs;
use std::io;
fn read_config(path: &str) -> io::Result<String> {
fs::read_to_string(path)
}
fn main() {
match read_config("missing-config.toml") {
Ok(text) => println!("{}", text),
Err(e) => println!("Konfiguration nicht lesbar: {}", e),
}
}

Ausgabe:

Konfiguration nicht lesbar: No such file or directory (os error 2)
▶ Selbst ausprobieren

Programme, die das Crate anyhow benutzen, schreiben anyhow::Result<T> auf dieselbe Weise. Der umgekehrte Fehler ist ebenfalls E0107: io::Result<String, io::Error> ergibt type alias takes 1 generic argument but 2 generic arguments were supplied, weil der Alias den Fehlertyp schon enthält. Streich den zweiten. Siehe Dateien lesen und schreiben und anyhow und thiserror.

Lösung 3: Deine Funktion generisch machen oder die Typen nennen

Abschnitt betitelt „Lösung 3: Deine Funktion generisch machen oder die Typen nennen“

Nimm diese Lösung, wenn der Typ dein eigenes generisches Struct ist und du nur seinen Namen geschrieben hast. rustc sagt missing generics for struct `Pair` (fehlende Generics für das Struct Pair):

error[E0107]: missing generics for struct `Pair`
--> src/main.rs:6:13
|
6 | fn show(p: &Pair) { // ❌ welche `K` und `V`?
| ^^^^ expected 2 generic arguments
|
note: struct defined here, with 2 generic parameters: `K`, `V`
--> src/main.rs:1:8
|
1 | struct Pair<K, V> {
| ^^^^ - -
help: add missing generic arguments
|
6 | fn show(p: &Pair<K, V>) { // ❌ welche `K` und `V`?
| ++++++

Das fehlerhafte Programm:

struct Pair<K, V> {
key: K,
value: V,
}
fn show(p: &Pair) { // ❌ welche `K` und `V`?
println!("{} = {}", p.key, p.value);
}
fn main() {
show(&Pair { key: "volume", value: 7 });
show(&Pair { key: "theme", value: "dark" });
}

Das help: schreibt Pair<K, V>, aber K und V gibt es in show noch nicht, also musst du sie auch deklarieren. Da show mit zwei verschiedenen Arten von Pair aufgerufen wird, mach die Funktion generisch, mit den Bounds, die sie zum Ausgeben braucht:

use std::fmt::Display;
struct Pair<K, V> {
key: K,
value: V,
}
fn show<K: Display, V: Display>(p: &Pair<K, V>) {
println!("{} = {}", p.key, p.value);
}
fn main() {
show(&Pair { key: "Lautstärke", value: 7 });
show(&Pair { key: "Design", value: "dunkel" });
}

Ausgabe:

Lautstärke = 7
Design = dunkel
▶ Selbst ausprobieren

Behandelt die Funktion immer nur eine Art, nenne stattdessen die Typen: fn show(p: &Pair<&str, u32>). Dasselbe gilt für impl Pair { ... }, das impl<K, V> Pair<K, V> { ... } sein muss. Siehe Generics.

Nimm diese Lösung, wenn rustc „but 2 generic arguments were supplied“ sagt und weniger will. Das passiert oft mit dem Turbofish ::<> bei einem Funktionsaufruf:

error[E0107]: function takes 1 generic argument but 2 generic arguments were supplied
--> src/main.rs:13:20
|
13 | println!("{}", largest::<i32, i32>(&scores)); // ❌ ein Typ zu viel
| ^^^^^^^ ----- help: remove the unnecessary generic argument
| |
| expected 1 generic argument
|
note: function defined here, with 1 generic parameter: `T`
--> src/main.rs:1:4
|
1 | fn largest<T: PartialOrd + Copy>(items: &[T]) -> T {
| ^^^^^^^ -

Die Hilfe sagt: „Entferne das unnötige generische Argument.“

Das fehlerhafte Programm:

fn largest<T: PartialOrd + Copy>(items: &[T]) -> T {
let mut best = items[0];
for &item in items {
if item > best {
best = item;
}
}
best
}
fn main() {
let scores = [72, 95, 88];
println!("{}", largest::<i32, i32>(&scores)); // ❌ ein Typ zu viel
}

largest hat einen Parameter, T, also nimmt es einen Typ. Schreib largest::<i32>(...), oder lass den Turbofish ganz weg, denn Rust erkennt T am Argument:

fn largest<T: PartialOrd + Copy>(items: &[T]) -> T {
let mut best = items[0];
for &item in items {
if item > best {
best = item;
}
}
best
}
fn main() {
let scores = [72, 95, 88];
println!("{}", largest(&scores));
println!("{}", largest::<f64>(&[1.5, 0.5]));
}

Ausgabe:

95
1.5
▶ Selbst ausprobieren

Das note: ist hier die nützlichste Zeile: Es zeigt die Definition und listet ihre Parameter auf, sodass du selbst nachzählen kannst.

HashMap<String> nur mit dem Schlüssel. rustc sagt struct takes at least 2 generic arguments but 1 generic argument was supplied. Lösung: HashMap<String, u32>, oder schreib HashMap::new() und lass Rust beide Typen aus dem ersten insert ableiten. Siehe HashMap.

Dein eigenes Struct mit zwei Typparametern, aber nur einem angegeben. let p: Pair<&str> = ... ergibt struct takes 2 generic arguments but 1 generic argument was supplied. Lösung: Pair<&str, i32>, oder Pair<_, _>, damit Rust beide einsetzt. Option<i32, i32> ist das Gegenteil: enum takes 1 generic argument but 2 generic arguments were supplied. Wolltest du zwei Werte, nimm ein Tupel: Option<(i32, i32)>.

Auch Lifetimes zählen. Ein Struct Excerpt<'a>, geschrieben als Excerpt<'a, 'a>, ergibt struct takes 1 lifetime argument but 2 lifetime arguments were supplied. Lösung: eine Lifetime pro Parameter der Definition. Siehe Lifetimes. Eine Methode, die gar nicht generisch ist, wie .into::<String>(), ergibt method takes 0 generic arguments but 1 generic argument was supplied; schreib stattdessen let s: String = x.into(); (siehe E0283).

Eine Überraschung: Vec<i32, i32> ist nicht E0107. Vec hat einen zweiten, instabilen Parameter für den Speicher-Allocator, darum bekommst du stattdessen E0658 (auf Englisch; allocator_api) und ein paar andere Fehler. Die Lösung ist dieselbe: Vec<i32>, oder Vec<(i32, i32)> für Paare.

  • E0061: derselbe Fehler mit normalen Argumenten, eine Funktion nimmt N Argumente, aber M wurden angegeben.
  • E0106: fehlender Lifetime-Bezeichner, wenn eine Referenz in einem Typ ein 'a braucht.
  • E0252: Zwei use-Zeilen importieren denselben Namen; oft folgt ein E0107, wenn Result dann fmt::Result bedeutet.
  • E0283: type annotations needed, die richtige Lösung, wenn .into::<T>() E0107 ergibt.
  • E0401: Eine innere Funktion benutzt das generische T der äußeren Funktion.
  • Alle Compilerfehler: die vollständige Liste der Fehlerseiten.

Kurzer Check

1. fn load() -> Result<String> ergibt E0107. Welcher Rückgabetyp behebt das für eine Funktion, die eine Datei liest?

2. Warum sagt rustc bei HashMap at least 2 generic arguments?

3. fn show(p: &Pair) ergibt `missing generics for struct Pair , und show wird mit verschiedenen Arten von Pair` aufgerufen. Was ist die Lösung?

Punkte: 0 / 3