Erreur Rust E0057 : this function takes N arguments but M were supplied (closures)
E0057 veut dire que vous avez appelé une closure (fonction anonyme) avec le mauvais nombre d’arguments : trop peu ou trop. C’est la version closure de E0061, et le message est presque identique. La correction est en général rapide : regardez les |...| de la closure et passez une valeur par paramètre. Cette page couvre aussi deux proches parentes que vous croiserez avec les itérateurs, E0593 et E0631, parce qu’elles viennent de la même confusion. Pour les autres erreurs de cette série, voir Les erreurs du compilateur Rust.
Le message d’erreur
Section intitulée « Le message d’erreur »error[E0057]: this function takes 2 arguments but 1 argument was supplied --> src/main.rs:3:17 |3 | let total = add(1); // ❌ add veut deux nombres | ^^^--- argument #2 of type `i32` is missing |note: closure defined here --> src/main.rs:2:15 |2 | let add = |a: i32, b: i32| a + b; | ^^^^^^^^^^^^^^^^help: provide the argument |3 | let total = add(1, /* i32 */); // ❌ add veut deux nombres | +++++++++++En français : « cette fonction prend 2 arguments, mais 1 seul a été fourni ». L’étiquette indique que l’argument n° 2, de type i32, manque, et l’aide montre où l’ajouter.
Voici le programme qui la provoque :
fn main() { let add = |a: i32, b: i32| a + b; let total = add(1); // ❌ add veut deux nombres println!("{total}");}Trop d’arguments donne le même code, avec une étiquette « unexpected argument » (argument inattendu) et une aide « remove the extra argument » (retirez l’argument en trop) :
error[E0057]: this function takes 1 argument but 2 arguments were supplied --> src/main.rs:3:5 |3 | greet("Ferris", "Corro"); | ^^^^^ ------- unexpected argument #2 of type `&'static str` |note: closure defined hereCe que ça veut dire
Section intitulée « Ce que ça veut dire »Lisez le message comme celui des fonctions : la première ligne compte (la closure en prend 2, vous en avez donné 1), l’étiquette sous votre appel nomme l’argument manquant ou en trop, et help: montre où il doit aller. /* i32 */ n’est qu’un commentaire qui vous indique le type. Mettez une vraie valeur à sa place.
E0057 ou E0061 ? La première ligne est la même. C’est la note: qui fait la différence :
closure defined here: vous avez appelé une closure. C’est E0057.callable defined here: vous avez appelé unfgénérique avec une contrainte commeF: Fn(i32). C’est aussi E0057.function defined here: unefnnormale, une méthode, ou un pointeur de fonction (let f: fn(i32, i32) -> i32 = add;). C’est E0061.
Dans tous les cas, la correction est la même : faites correspondre l’appel à la liste des paramètres.
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »La liste des paramètres d’une closure est aussi figée que celle d’une fonction. |a: i32, b: i32| prend exactement deux i32, à chaque fois. Il n’y a pas d’arguments par défaut ni de « passez-en moins si vous voulez ». Même quand vous omettez les types, rustc les déduit du corps de la closure et de sa première utilisation, et ensuite ils sont fixés.
Sous le capot, une closure implémente l’un des traits Fn avec une liste fixe de types d’arguments, comme Fn(i32, i32) -> i32. Le compilateur vérifie chaque appel par rapport à cette liste : un appel avec le mauvais nombre d’arguments n’atteint donc jamais votre programme en cours d’exécution. Le fonctionnement des closures et de leurs paramètres est expliqué dans Les closures et Les traits de closure.
Comment corriger
Section intitulée « Comment corriger »Solution 1 : faire correspondre l’appel aux paramètres de la closure
Section intitulée « Solution 1 : faire correspondre l’appel aux paramètres de la closure »Choisissez cette solution presque à chaque fois. Allez à l’emplacement indiqué par la note:, comptez les paramètres entre les barres |...|, et passez une valeur pour chacun. Si vous avez plus de valeurs que de paramètres, appelez la closure plusieurs fois :
fn main() { let add = |a: i32, b: i32| a + b; let greet = |name: &str| println!("Bonjour, {name} !");
let total = add(1, 2); println!("{total}");
greet("Ferris"); greet("Corro"); // un appel par nom}Sortie :
3Bonjour, Ferris !Bonjour, Corro !Si la closure doit vraiment prendre un autre nombre de valeurs, modifiez plutôt la closure. C’est très bien aussi, du moment que chaque appel est d’accord avec elle.
Solution 2 : les tuples (un tuple est un seul argument)
Section intitulée « Solution 2 : les tuples (un tuple est un seul argument) »Choisissez cette solution quand des tuples sont en jeu. |(name, score)| et |name, score| se ressemblent, mais ce sont deux closures différentes. La première prend un argument, un tuple, et le décompose. La seconde prend deux arguments.
Appeler une closure à tuple avec deux valeurs donne E0057, et rustc suggère d’ajouter les parenthèses :
error[E0057]: function takes 1 argument but 2 arguments were supplied --> src/main.rs:3:5 |3 | show("Ferris", 90); | ^^^^ |note: closure parameter defined herehelp: wrap these arguments in parentheses to construct a tuple |3 | show(("Ferris", 90)); | + +L’aide dit : « entourez ces arguments de parenthèses pour construire un tuple ».
L’erreur inverse arrive tout le temps avec les itérateurs. zip, enumerate et le parcours d’une map donnent à votre closure un tuple par élément. Écrire .map(|name, score| ...) donne un autre code, E0593 :
error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments --> src/main.rs:7:10 |7 | .map(|name, score| format!("{name}: {score}")) | ^^^ ------------- takes 2 distinct arguments | | | expected closure that takes a single 2-tuple as argument |help: change the closure to accept a tuple instead of individual arguments |7 - .map(|name, score| format!("{name}: {score}"))7 + .map(|(name, score)| format!("{name}: {score}"))En français : « la closure devrait prendre un seul tuple de 2 éléments comme argument, mais elle prend 2 arguments distincts ». L’aide propose de lui faire accepter un tuple.
Les deux ont la même correction : mettez les parenthèses là où se trouve le tuple.
fn main() { let names = vec!["Ferris", "Corro"]; let scores = vec![90, 75];
// zip donne un tuple (name, score) par élément : décomposez-le dans la liste des paramètres let lines: Vec<String> = names .iter() .zip(scores.iter()) .map(|(name, score)| format!("{name}: {score}")) .collect(); println!("{:?}", lines);
// une closure qui prend un tuple s'appelle avec un seul tuple let show = |(name, score): (&str, i32)| println!("{name} a obtenu {score}"); show(("Ferris", 90));}Sortie :
["Ferris: 90", "Corro: 75"]Ferris a obtenu 90C’est pareil pour .enumerate().map(|(i, item)| ...) et map.iter().for_each(|(key, value)| ...). Voir Les itérateurs et Les tuples.
Solution 3 : adapter avec une closure d’enrobage
Section intitulée « Solution 3 : adapter avec une closure d’enrobage »Choisissez cette solution quand vous passez une fonction ou une closure à une autre fonction (une « fonction d’ordre supérieur ») et que le nombre de paramètres ne correspond pas. Ici, apply veut quelque chose qui prend un i32, mais add en prend deux. Ça donne de nouveau E0593 :
error[E0593]: function is expected to take 1 argument, but it takes 2 arguments --> src/main.rs:10:26 | 5 | fn add(a: i32, b: i32) -> i32 { | ----------------------------- takes 2 arguments...10 | println!("{}", apply(add, 5)); | ----- ^^^ expected function that takes 1 argument | | | required by a bound introduced by this callAutrement dit : apply attend une fonction à 1 argument, mais add en prend 2.
Ne modifiez pas add. Enveloppez-la dans une petite closure qui a la forme qu’attend apply et qui fournit elle-même la valeur manquante :
fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x)}
fn add(a: i32, b: i32) -> i32 { a + b}
fn main() { let bonus = 10; // prend 1 argument, appelle add avec 2 println!("{}", apply(|x| add(x, bonus), 5));
// prend 1 tuple, appelle add avec ses deux parties let pairs = vec![(1, 2), (3, 4)]; let sums: Vec<i32> = pairs.into_iter().map(|(a, b)| add(a, b)).collect(); println!("{:?}", sums);}Sortie :
15[3, 7]La closure d’enrobage emprunte bonus au code qui l’entoure, et c’est exactement ce que les closures savent bien faire. Passer une fonction par son nom (apply(double, 5)) ne fonctionne que si ses paramètres correspondent déjà. Voir Les traits de closure.
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »Dans une fonction générique. Une fn call_twice<F: Fn(i32)>(f: F) qui appelle f(2, 3) donne E0057 avec note: callable defined here, qui pointe vers Fn(i32). La contrainte, c’est la liste des paramètres. Correction : appelez f(2), ou changez la contrainte en Fn(i32, i32) si vous avez vraiment besoin de deux valeurs (alors la closure de chaque appelant devra en prendre deux).
Les closures dans une Box. let f: Box<dyn Fn(i32, i32) -> i32> = Box::new(|a, b| a + b); puis f(1) donne E0057 avec note: implementation defined here. Correction : passez les deux valeurs, f(1, 2).
Le tri. v.sort_by(|a| a.cmp(a)) donne E0593, « closure is expected to take 2 arguments, but it takes 1 argument ». sort_by compare deux éléments à la fois. Correction : v.sort_by(|a, b| a.cmp(b)). Voir Le tri.
Le bon nombre, mais le mauvais type. nums.iter().map(|x: i32| x * 2) donne E0631, « type mismatch in closure arguments » (types incompatibles dans les arguments de la closure), parce que .iter() fournit des &i32. L’aide de rustc suggère |x: &i32|. Correction la plus simple : retirez l’annotation, |x| x * 2.
Décomposer quelque chose qui n’est pas un tuple. v.iter().map(|(a, b)| a + b) sur un simple Vec<i32> donne E0308, « expected integer, found (_, _) ». Correction : n’utilisez |(a, b)| que si les éléments sont vraiment des paires.
Erreurs liées
Section intitulée « Erreurs liées »- E0061 : mauvais nombre d’arguments : le même message pour les fonctions normales, les méthodes et les pointeurs de fonction.
- E0308 : mismatched types : le nombre est bon, mais un argument (ou un motif de tuple) a le mauvais type.
- E0373 : closure may outlive the current function : une autre erreur courante avec les closures, à propos des variables capturées dans les threads et des closures renvoyées.
- E0599 : no method found : peut apparaître à côté de E0631, parce qu’une closure avec la mauvaise signature casse le reste d’une chaîne d’itérateurs.
- E0593 : closure takes 2 distinct arguments : une closure ou une fonction passée avec le mauvais nombre de paramètres.
- E0631 : type mismatch in closure arguments : le bon nombre de paramètres, mais le mauvais type.
- Toutes les erreurs du compilateur : la liste complète des pages d’erreurs.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. let double = |x: i32| x * 2; est appelée avec double(2, 3). Que signale rustc ?
2. names.iter().zip(ages.iter()).map(|n, a| ...) ne compile pas. Quelle est la correction ?
3. E0057 et E0061 disent toutes les deux this function takes 2 arguments but 1 argument was supplied. Comment savoir que vous avez appelé une closure ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0057 — Rust error codes index (en anglais) : l’explication officielle de cette erreur.
- Closures — The Rust Book (en anglais) : la syntaxe des closures, l’inférence de type pour les paramètres et les traits
Fn. - Closures — Rust by Example (en anglais) : de courts exemples exécutables de closures, y compris passées à des fonctions.
std::ops::Fn— documentation de la bibliothèque standard (en anglais) : comment une contrainteFn(i32) -> i32décrit les paramètres d’une closure.- E0593 — Rust error codes index (en anglais) : l’explication officielle de l’erreur « closure passée avec le mauvais nombre de paramètres ».