Errore Rust E0057: this function takes N arguments but M were supplied (closure)
E0057 significa che hai chiamato una closure con il numero sbagliato di argomenti: troppo pochi o troppi. È la versione per le closure di E0061, e il messaggio si legge quasi uguale. La soluzione di solito è rapida: guarda le |...| della closure e passa un valore per ogni parametro. Questa pagina copre anche due parenti stretti che incontrerai con gli iteratori, E0593 ed E0631, perché nascono dalla stessa confusione. Per gli altri errori di questa serie, vedi Errori del compilatore Rust.
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0057]: this function takes 2 arguments but 1 argument was supplied --> src/main.rs:3:17 |3 | let total = add(1); // ❌ add wants two numbers | ^^^--- 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 wants two numbers | +++++++++++Il compilatore dice: «questa funzione prende 2 argomenti, ma ne è stato fornito 1», e la nota indica la closure. Questo è il programma che lo produce:
fn main() { let add = |a: i32, b: i32| a + b; let total = add(1); // ❌ add wants two numbers println!("{total}");}Troppi argomenti danno lo stesso codice, con un’etichetta «unexpected argument» (argomento inatteso) e un help «remove the extra argument» (togli l’argomento in più):
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 hereCosa significa
Sezione intitolata “Cosa significa”Leggi il messaggio come quello per le funzioni: la prima riga conta (la closure ne prende 2, tu ne hai dato 1), l’etichetta sotto la tua chiamata nomina l’argomento mancante o in più, e help: mostra dove va. /* i32 */ è solo un commento segnaposto che ti dice il tipo. Mettici un valore vero.
E0057 o E0061? La prima riga è la stessa. È la note: a distinguerli:
closure defined here: hai chiamato una closure. È E0057.callable defined here: hai chiamato unfgenerico con un vincolo comeF: Fn(i32). Anche questo è E0057.function defined here: una normalefn, un metodo o un puntatore a funzione (let f: fn(i32, i32) -> i32 = add;). È E0061.
In ogni caso la soluzione è la stessa: fai corrispondere la chiamata all’elenco dei parametri.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”L’elenco dei parametri di una closure è fisso quanto quello di una funzione. |a: i32, b: i32| prende esattamente due i32, ogni volta. Non ci sono argomenti predefiniti né «passane meno se vuoi». Anche quando ometti i tipi, rustc li ricava dal corpo della closure e dal suo primo utilizzo, e da quel momento restano bloccati.
Dietro le quinte, una closure implementa uno dei trait Fn con un elenco fisso di tipi di argomenti, come Fn(i32, i32) -> i32. Il compilatore controlla ogni chiamata rispetto a quell’elenco, quindi una chiamata con il numero sbagliato non arriva mai al programma in esecuzione. Come funzionano le closure e i loro parametri lo trovi in Closure e Fn, FnMut e FnOnce.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: Fai corrispondere la chiamata ai parametri della closure
Sezione intitolata “Soluzione 1: Fai corrispondere la chiamata ai parametri della closure”Sceglila quasi sempre. Vai al punto indicato dalla note:, conta i parametri tra le barre |...| e passa un valore per ciascuno. Se hai più valori che parametri, chiama la closure più di una volta:
fn main() { let add = |a: i32, b: i32| a + b; let greet = |name: &str| println!("Ciao, {name}!");
let total = add(1, 2); println!("{total}");
greet("Ferris"); greet("Corro"); // una chiamata per ogni nome}Output:
3Ciao, Ferris!Ciao, Corro!Se la closure deve davvero prendere un numero diverso di valori, cambia invece la closure. Va benissimo anche così, purché ogni chiamata sia d’accordo con lei.
Soluzione 2: Tuple: una tupla è un solo argomento
Sezione intitolata “Soluzione 2: Tuple: una tupla è un solo argomento”Scegli questa quando ci sono di mezzo le tuple. |(name, score)| e |name, score| si somigliano, ma sono closure diverse. La prima prende un argomento, una tupla, e lo spacchetta. La seconda prende due argomenti.
Chiamare una closure con tupla passando due valori dà E0057, e rustc suggerisce di aggiungere le parentesi:
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’errore opposto capita di continuo con gli iteratori. zip, enumerate e l’iterazione sulle mappe passano alla tua closure una tupla per elemento. Scrivere .map(|name, score| ...) dà un codice diverso, 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}"))Qui rustc dice che si aspettava una closure con una sola tupla di 2 elementi, ma la tua prende 2 argomenti distinti. Entrambi si risolvono allo stesso modo: metti le parentesi dove c’è la tupla.
fn main() { let names = vec!["Ferris", "Corro"]; let scores = vec![90, 75];
// zip dà una tupla (name, score) per elemento: spacchettala nell'elenco dei parametri let lines: Vec<String> = names .iter() .zip(scores.iter()) .map(|(name, score)| format!("{name}: {score}")) .collect(); println!("{:?}", lines);
// una closure che prende una tupla si chiama con una tupla let show = |(name, score): (&str, i32)| println!("{name} ha fatto {score} punti"); show(("Ferris", 90));}Output:
["Ferris: 90", "Corro: 75"]Ferris ha fatto 90 puntiLo stesso vale per .enumerate().map(|(i, item)| ...) e per map.iter().for_each(|(key, value)| ...). Vedi Iteratori e Tuple.
Soluzione 3: Adatta con una closure di appoggio
Sezione intitolata “Soluzione 3: Adatta con una closure di appoggio”Scegli questa quando passi una funzione o una closure a un’altra funzione (una «funzione di ordine superiore») e il numero dei parametri non torna. Qui apply vuole qualcosa che prende un i32, ma add ne prende due. Di nuovo 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 callNon cambiare add. Avvolgila in una piccola closure che ha la forma che apply si aspetta e che completa da sola il valore mancante:
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; // prende 1 argomento, chiama add con 2 println!("{}", apply(|x| add(x, bonus), 5));
// prende 1 tupla, chiama add con le sue due parti let pairs = vec![(1, 2), (3, 4)]; let sums: Vec<i32> = pairs.into_iter().map(|(a, b)| add(a, b)).collect(); println!("{:?}", sums);}Output:
15[3, 7]La closure di appoggio prende in prestito bonus dal codice che la circonda, ed è proprio ciò in cui le closure sono brave. Passare una funzione per nome (apply(double, 5)) funziona solo quando i suoi parametri corrispondono già. Vedi Fn, FnMut e FnOnce.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Dentro una funzione generica. fn call_twice<F: Fn(i32)>(f: F) che chiama f(2, 3) dà E0057 con note: callable defined here, che indica Fn(i32). Il vincolo è l’elenco dei parametri. Soluzione: chiama f(2), oppure cambia il vincolo in Fn(i32, i32) se ti servono davvero due valori (e allora la closure di ogni chiamante dovrà prenderne due).
Closure in un Box. let f: Box<dyn Fn(i32, i32) -> i32> = Box::new(|a, b| a + b); e poi f(1) dà E0057 con note: implementation defined here. Soluzione: passa entrambi i valori, f(1, 2).
L’ordinamento. v.sort_by(|a| a.cmp(a)) dà E0593, «closure is expected to take 2 arguments, but it takes 1 argument». sort_by confronta due elementi alla volta. Soluzione: v.sort_by(|a, b| a.cmp(b)). Vedi Ordinamento.
Numero giusto, tipo sbagliato. nums.iter().map(|x: i32| x * 2) dà E0631, «type mismatch in closure arguments» (tipi non corrispondenti negli argomenti della closure), perché .iter() passa &i32. L’help di rustc suggerisce |x: &i32|. La soluzione più semplice: togli l’annotazione, |x| x * 2.
Spacchettare qualcosa che non è una tupla. v.iter().map(|(a, b)| a + b) su un semplice Vec<i32> dà E0308, «expected integer, found (_, _)». Soluzione: usa |(a, b)| solo quando gli elementi sono davvero delle coppie.
Errori correlati
Sezione intitolata “Errori correlati”- E0061: wrong number of arguments: lo stesso messaggio per funzioni normali, metodi e puntatori a funzione.
- E0308: mismatched types: il numero è giusto, ma un argomento (o un pattern di tupla) ha il tipo sbagliato.
- E0373: closure may outlive the current function: un altro errore comune con le closure, sulle variabili catturate nei thread e nelle closure restituite.
- E0599: no method found: può comparire accanto a E0631, perché una closure con la firma sbagliata rompe il resto di una catena di iteratori.
- E0593: closure takes 2 distinct arguments: una closure o una funzione passata con il numero sbagliato di parametri.
- E0631: type mismatch in closure arguments: il numero di parametri è giusto, ma il tipo è sbagliato.
- Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. let double = |x: i32| x * 2; viene chiamata come double(2, 3). Cosa segnala rustc?
2. names.iter().zip(ages.iter()).map(|n, a| ...) non compila. Qual è la soluzione?
3. Sia E0057 sia E0061 dicono this function takes 2 arguments but 1 argument was supplied. Come capisci che hai chiamato una closure?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0057 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
- Closures — The Rust Book (in inglese): la sintassi delle closure, l’inferenza di tipo per i parametri e i trait
Fn. - Closures — Rust by Example (in inglese): brevi esempi eseguibili di closure, compreso come passarle alle funzioni.
std::ops::Fn— standard library docs (in inglese): come i vincoliFn(i32) -> i32descrivono i parametri di una closure.- E0593 — Rust error codes index (in inglese): la spiegazione ufficiale dell’errore della closure passata con il numero sbagliato di parametri.