Aller au contenu

Erreur Rust E0107 : takes N generic arguments but M were supplied (nombre de types)

E0107 veut dire que le nombre de types entre <...> ne correspond pas à ce qu’attend le type ou la fonction. Result en veut deux (Result<T, E>), HashMap en veut deux (HashMap<K, V>), Option et Vec en veulent un. Donnez-en un de trop ou un de moins, et rustc dit quelque chose comme enum takes 2 generic arguments but 1 generic argument was supplied (l’enum prend 2 arguments génériques, mais 1 seul a été fourni), avec « struct », « enum », « type alias », « function » ou « method » au début. C’est l’une des erreurs les plus sympathiques : rustc vous donne les deux nombres, et la correction consiste à ajouter ou à retirer un type. Cette page fait partie du guide des erreurs du compilateur Rust.

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` a besoin de deux types
| ^^^^^^ --- supplied 1 generic argument
| |
| expected 2 generic arguments
|
help: add missing generic argument
|
1 | fn parse_age(text: &str) -> Result<u32, E> { // ❌ `Result` a besoin de deux types
| +++

Voici le programme qui la provoque :

fn parse_age(text: &str) -> Result<u32> { // ❌ `Result` a besoin de deux types
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 pointe deux fois la ligne 1 :

  1. Les ^^^^^^ sous Result : « expected 2 generic arguments » (2 arguments génériques attendus). Result est un enum avec deux paramètres de type : T pour la valeur en cas de succès et E pour l’erreur.
  2. Les --- sous u32 : « supplied 1 generic argument » (1 argument générique fourni). Vous n’avez donné que T.
  3. La ligne help: ajoute un E provisoire (« add missing generic argument », ajoutez l’argument générique manquant). C’est une indication sur l’endroit où va le type, pas le type lui-même : vous remplacez E par le vrai type d’erreur.

Les arguments génériques sont comme les cases d’un formulaire. Un formulaire Result a deux cases, « si ça marche : » et « si ça échoue : ». Remplissez-en une seule, et le guichetier vous le rend.

Cette erreur est très courante si vous avez lu du code qui écrit Result<u32> avec un seul type. Ce code n’était pas faux : il utilisait un alias de type comme io::Result<T> ou anyhow::Result<T>, qui remplit le type d’erreur à votre place. Le Result simple du prélude n’a pas ce raccourci.

Un type générique n’est pas un type complet tant que chaque paramètre n’a pas de valeur. Result<u32, E> avec un E inconnu ne dit ni quelle taille fait la valeur, ni à quoi ressemble une erreur, donc Rust ne peut pas compiler le code qui l’utilise. Rust pourrait essayer de deviner le type manquant à partir du corps de la fonction, mais il ne déduit volontairement jamais les types dans la signature d’une fonction. Les signatures sont le contrat sur lequel s’appuie le reste du code, elles doivent donc être écrites en entier.

Certains paramètres ont une valeur par défaut. HashMap<K, V, S = RandomState> a un troisième paramètre pour la fonction de hachage, c’est pour ça que rustc dit « at least 2 » (au moins 2) pour un HashMap. Les valeurs par défaut ne peuvent compléter que depuis la fin, et seulement là où l’auteur du type en a prévu une. Voir Les génériques et Result en Rust.

Choisissez-la la plupart du temps. Trouvez ce que doit être le paramètre manquant et écrivez-le. Pour parse(), le type d’erreur d’un nombre qui n’a pas pu être lu est std::num::ParseIntError :

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

Sortie :

âge : 42
erreur : invalid digit found in string
▶ Essayez par vous-même

Le message « invalid digit found in string » (chiffre invalide dans la chaîne) vient de la bibliothèque standard, qui l’écrit toujours en anglais. Vous ne savez pas quel est le type d’erreur ? Regardez la documentation de la fonction dont vous renvoyez l’erreur. La même correction marche pour HashMap<String> : dites ce que sont les valeurs, comme dans HashMap<String, u32>. Voir HashMap en Rust et Les erreurs personnalisées.

Solution 2 : utiliser l’alias à un seul type que vous vouliez, comme io::Result

Section intitulée « Solution 2 : utiliser l’alias à un seul type que vous vouliez, comme io::Result »

Choisissez-la quand vous travaillez avec des fichiers, le réseau ou l’entrée standard, ou quand vous avez vu Result<T> dans le code de quelqu’un d’autre. std::io::Result<T> est un raccourci pour Result<T, std::io::Error>. Importez le module et écrivez 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!("impossible de lire la config : {}", e),
}
}

Sortie :

impossible de lire la config : No such file or directory (os error 2)
▶ Essayez par vous-même

« No such file or directory » (fichier ou dossier introuvable) est le message du système d’exploitation. Les applications qui utilisent la crate anyhow écrivent anyhow::Result<T> de la même façon. L’erreur inverse donne aussi E0107 : io::Result<String, io::Error> donne type alias takes 1 generic argument but 2 generic arguments were supplied (l’alias de type prend 1 argument générique, mais 2 ont été fournis), parce que l’alias contient déjà le type d’erreur. Retirez le second. Voir Lire et écrire des fichiers et anyhow et thiserror.

Solution 3 : rendre votre fonction générique, ou nommer les types

Section intitulée « Solution 3 : rendre votre fonction générique, ou nommer les types »

Choisissez-la quand le type est votre propre struct générique et que vous n’avez écrit que son nom. rustc dit missing generics for struct `Pair` (génériques manquants pour la struct Pair) :

error[E0107]: missing generics for struct `Pair`
--> src/main.rs:6:13
|
6 | fn show(p: &Pair) { // ❌ quels `K` et `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>) { // ❌ quels `K` et `V` ?
| ++++++

Le programme qui la provoque :

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

La ligne help: écrit Pair<K, V>, mais K et V n’existent pas encore dans show, vous devez donc aussi les déclarer. Comme show est appelée avec deux sortes de Pair différentes, rendez-la générique, avec les contraintes (bounds) dont elle a besoin pour afficher :

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: "volume", value: 7 });
show(&Pair { key: "thème", value: "sombre" });
}

Sortie :

volume = 7
thème = sombre
▶ Essayez par vous-même

Si la fonction ne traite jamais qu’une seule sorte, nommez plutôt les types : fn show(p: &Pair<&str, u32>). C’est pareil pour impl Pair { ... }, qui doit s’écrire impl<K, V> Pair<K, V> { ... }. Voir Les génériques.

Choisissez-la quand rustc dit « but 2 generic arguments were supplied » (mais 2 arguments génériques ont été fournis) et en veut moins. Ça arrive souvent avec le turbofish ::<> sur un appel de fonction :

error[E0107]: function takes 1 generic argument but 2 generic arguments were supplied
--> src/main.rs:13:20
|
13 | println!("{}", largest::<i32, i32>(&scores)); // ❌ un type de trop
| ^^^^^^^ ----- 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 {
| ^^^^^^^ -

Le programme qui la provoque :

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)); // ❌ un type de trop
}

largest a un seul paramètre, T, donc elle prend un seul type. Écrivez largest::<i32>(...), ou laissez complètement tomber le turbofish, puisque Rust peut deviner T à partir de l’argument (l’aide de rustc dit d’ailleurs de retirer l’argument générique inutile) :

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]));
}

Sortie :

95
1.5
▶ Essayez par vous-même

La note: est la ligne la plus utile ici : elle montre la définition et liste ses paramètres, pour que vous puissiez les compter vous-même.

HashMap<String> avec seulement la clé. rustc dit struct takes at least 2 generic arguments but 1 generic argument was supplied. Correction : HashMap<String, u32>, ou écrivez HashMap::new() et laissez Rust déduire les deux à partir du premier insert. Voir HashMap en Rust.

Votre propre struct à deux paramètres de type, avec un seul. let p: Pair<&str> = ... donne struct takes 2 generic arguments but 1 generic argument was supplied. Correction : Pair<&str, i32>, ou Pair<_, _> pour laisser Rust remplir les deux. Option<i32, i32>, c’est l’inverse : enum takes 1 generic argument but 2 generic arguments were supplied. Si vous vouliez deux valeurs, utilisez un tuple, Option<(i32, i32)>.

Les lifetimes comptent aussi. Une struct Excerpt<'a> écrite Excerpt<'a, 'a> donne struct takes 1 lifetime argument but 2 lifetime arguments were supplied. Correction : une lifetime (durée de vie) par paramètre de la définition. Voir Les lifetimes. Une méthode qui n’est pas du tout générique, comme .into::<String>(), donne method takes 0 generic arguments but 1 generic argument was supplied ; écrivez plutôt let s: String = x.into(); (voir E0283).

Une surprise : Vec<i32, i32> ne donne pas E0107. Vec a un second paramètre, instable, pour l’allocateur de mémoire, donc vous obtenez à la place E0658 (en anglais, allocator_api) et quelques autres erreurs. La correction est la même : Vec<i32>, ou Vec<(i32, i32)> pour des paires.

  • E0061 : la même erreur avec des arguments normaux, une fonction qui prend N arguments alors que M ont été fournis.
  • E0106 : missing lifetime specifier, quand une référence dans un type a besoin d’un 'a.
  • E0252 : deux lignes use importent le même nom ; une E0107 suit souvent quand Result désigne fmt::Result.
  • E0283 : type annotations needed, la bonne correction quand .into::<T>() donne E0107.
  • E0401 : une fonction interne utilise le T générique de la fonction extérieure.
  • Toutes les erreurs du compilateur : la liste complète des pages d’erreurs.

Petit quiz

1. fn load() -> Result<String> donne E0107. Quel type de retour corrige ça pour une fonction qui lit un fichier ?

2. Pourquoi rustc dit-il at least 2 generic arguments pour HashMap ?

3. fn show(p: &Pair) donne `missing generics for struct Pair , et show est appelée avec différentes sortes de Pair`. Quelle est la correction ?

Score : 0 / 3