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.
Le message d’erreur
Section intitulée « Le message d’erreur »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), } }}Ce que ça veut dire
Section intitulée « Ce que ça veut dire »rustc pointe deux fois la ligne 1 :
- Les
^^^^^^sousResult: « expected 2 generic arguments » (2 arguments génériques attendus).Resultest un enum avec deux paramètres de type :Tpour la valeur en cas de succès etEpour l’erreur. - Les
---sousu32: « supplied 1 generic argument » (1 argument générique fourni). Vous n’avez donné queT. - La ligne
help:ajoute unEprovisoire (« 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 remplacezEpar 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.
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »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.
Comment corriger
Section intitulée « Comment corriger »Solution 1 : écrire le type manquant
Section intitulée « Solution 1 : écrire le type manquant »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 : 42erreur : invalid digit found in stringLe 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)« 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 = 7thème = sombreSi 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.
Solution 4 : retirer l’argument en trop
Section intitulée « Solution 4 : retirer l’argument en trop »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 :
951.5La 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.
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »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.
Erreurs liées
Section intitulée « Erreurs liées »- 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
useimportent le même nom ; une E0107 suit souvent quandResultdésignefmt::Result. - E0283 : type annotations needed, la bonne correction quand
.into::<T>()donne E0107. - E0401 : une fonction interne utilise le
Tgénérique de la fonction extérieure. - Toutes les erreurs du compilateur : la liste complète des pages d’erreurs.
Petit quiz
Section intitulée « Petit quiz »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
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0107 — Rust error codes index (en anglais) : l’explication officielle, avec des exemples de trop ou trop peu d’arguments génériques et de lifetimes.
- Generic Data Types — The Rust Book (en anglais) : comment
<T>marche dans des structs commePoint<T, U>, des enums commeResult<T, E>, et des méthodes. - Recoverable Errors with
Result— The Rust Book (en anglais) : des fonctions qui renvoientResult<String, io::Error>, et l’opérateur?. std::io::Result— standard library docs (en anglais) : l’alias à un seul paramètre, et pourquoi tant de code écritio::Result<T>.- Generic parameters — The Rust Reference (en anglais) : les règles exactes, y compris les paramètres de type par défaut comme la fonction de hachage de
HashMap.