Zum Inhalt springen

Rust-Fehler E0057: this function takes N arguments but M were supplied (Closures)

E0057 bedeutet: Du hast ein Closure (eine anonyme Funktion) mit der falschen Anzahl von Argumenten aufgerufen – mit zu wenigen oder zu vielen. Es ist die Closure-Variante von E0061, und die Meldung liest sich fast genauso. Die Lösung geht meistens schnell: Schau dir das |...| des Closures an und übergib einen Wert pro Parameter. Diese Seite behandelt auch zwei nahe Verwandte, die dir bei Iteratoren begegnen, E0593 und E0631, weil sie aus derselben Verwechslung entstehen. Die anderen Fehler dieser Reihe findest du unter Rust-Compilerfehler.

error[E0057]: this function takes 2 arguments but 1 argument was supplied
--> src/main.rs:3:17
|
3 | let total = add(1); // ❌ add will zwei Zahlen
| ^^^--- 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 will zwei Zahlen
| +++++++++++

Auf Deutsch heißt das ungefähr: „Diese Funktion nimmt 2 Argumente, aber es wurde 1 Argument übergeben.“ Dieses Programm erzeugt die Meldung:

fn main() {
let add = |a: i32, b: i32| a + b;
let total = add(1); // ❌ add will zwei Zahlen
println!("{total}");
}

Zu viele Argumente führen zum selben Code, mit einem Label „unexpected argument“ (unerwartetes Argument) und dem Hinweis „remove the extra argument“ (entferne das überzählige Argument):

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 here

Lies die Meldung wie die für Funktionen: Die erste Zeile zählt (das Closure nimmt 2, du hast 1 übergeben), das Label unter deinem Aufruf nennt das fehlende oder überzählige Argument, und help: zeigt, wo es hingehört. /* i32 */ ist nur ein Platzhalter-Kommentar, der dir den Typ nennt. Setz dort einen echten Wert ein.

E0057 oder E0061? Die erste Zeile ist gleich. Die note: verrät den Unterschied:

  • closure defined here (Closure hier definiert): Du hast ein Closure aufgerufen. Das ist E0057.
  • callable defined here (Aufrufbares hier definiert): Du hast ein generisches f mit einem Bound wie F: Fn(i32) aufgerufen. Auch E0057.
  • function defined here (Funktion hier definiert): eine normale fn, eine Methode oder ein Funktionszeiger (let f: fn(i32, i32) -> i32 = add;). Das ist E0061.

In jedem Fall ist die Lösung dieselbe: Bring den Aufruf mit der Parameterliste in Einklang.

Die Parameterliste eines Closures ist genauso fest wie die einer Funktion. |a: i32, b: i32| nimmt genau zwei i32, jedes Mal. Es gibt keine Standardargumente und kein „gib ruhig weniger, wenn du magst“. Selbst wenn du die Typen weglässt, leitet rustc sie aus dem Rumpf des Closures und seiner ersten Verwendung ab – und ab dann stehen sie fest.

Unter der Haube implementiert ein Closure einen der Fn-Traits mit einer festen Liste von Argumenttypen, etwa Fn(i32, i32) -> i32. Der Compiler prüft jeden Aufruf gegen diese Liste. Ein Aufruf mit der falschen Anzahl erreicht dein laufendes Programm also nie. Wie Closures und ihre Parameter funktionieren, erfährst du in Closures und Closure-Traits.

Lösung 1: Den Aufruf an die Parameter des Closures anpassen

Abschnitt betitelt „Lösung 1: Den Aufruf an die Parameter des Closures anpassen“

Nimm diese Lösung fast immer. Spring zur Stelle aus der note:, zähl die Parameter zwischen den senkrechten Strichen |...| und übergib für jeden einen Wert. Hast du mehr Werte als Parameter, ruf das Closure mehrmals auf:

fn main() {
let add = |a: i32, b: i32| a + b;
let greet = |name: &str| println!("Hallo, {name}!");
let total = add(1, 2);
println!("{total}");
greet("Ferris");
greet("Corro"); // ein Aufruf pro Name
}

Ausgabe:

3
Hallo, Ferris!
Hallo, Corro!
▶ Selbst ausprobieren

Soll das Closure wirklich eine andere Anzahl von Werten nehmen, ändere stattdessen das Closure. Auch das ist in Ordnung, solange jeder Aufruf dazu passt.

Nimm diese Lösung, wenn Tupel im Spiel sind. |(name, score)| und |name, score| sehen ähnlich aus, sind aber verschiedene Closures. Das erste nimmt ein Argument, ein Tupel, und packt es aus. Das zweite nimmt zwei Argumente.

Rufst du ein Tupel-Closure mit zwei Werten auf, bekommst du E0057, und rustc schlägt vor, Klammern zu ergänzen:

error[E0057]: function takes 1 argument but 2 arguments were supplied
--> src/main.rs:3:5
|
3 | show("Ferris", 90);
| ^^^^
|
note: closure parameter defined here
help: wrap these arguments in parentheses to construct a tuple
|
3 | show(("Ferris", 90));
| + +

Der umgekehrte Fehler passiert ständig bei Iteratoren. zip, enumerate und das Durchlaufen einer Map geben deinem Closure ein Tupel pro Element. Schreibst du .map(|name, score| ...), bekommst du einen anderen 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}"))

Auf Deutsch: „Das Closure soll ein einzelnes 2-Tupel als Argument nehmen, nimmt aber 2 einzelne Argumente.“ Beide Fehler haben dieselbe Lösung: Setz die Klammern dorthin, wo das Tupel ist.

fn main() {
let names = vec!["Ferris", "Corro"];
let scores = vec![90, 75];
// zip liefert ein (name, score)-Tupel pro Element: in der Parameterliste auspacken
let lines: Vec<String> = names
.iter()
.zip(scores.iter())
.map(|(name, score)| format!("{name}: {score}"))
.collect();
println!("{:?}", lines);
// ein Closure, das ein Tupel nimmt, wird mit einem Tupel aufgerufen
let show = |(name, score): (&str, i32)| println!("{name} hat {score} Punkte");
show(("Ferris", 90));
}

Ausgabe:

["Ferris: 90", "Corro: 75"]
Ferris hat 90 Punkte
▶ Selbst ausprobieren

Dasselbe gilt für .enumerate().map(|(i, item)| ...) und map.iter().for_each(|(key, value)| ...). Siehe Iteratoren und Tupel.

Nimm diese Lösung, wenn du eine Funktion oder ein Closure an eine andere Funktion übergibst (eine „Funktion höherer Ordnung“) und die Parameterzahlen nicht zusammenpassen. Hier will apply etwas, das ein i32 nimmt, aber add nimmt zwei. Das führt wieder zu 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 call

Ändere add nicht. Verpack es in ein kleines Closure, das die Form hat, die apply erwartet, und den fehlenden Wert selbst ergänzt:

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;
// nimmt 1 Argument, ruft add mit 2 auf
println!("{}", apply(|x| add(x, bonus), 5));
// nimmt 1 Tupel, ruft add mit seinen beiden Teilen auf
let pairs = vec![(1, 2), (3, 4)];
let sums: Vec<i32> = pairs.into_iter().map(|(a, b)| add(a, b)).collect();
println!("{:?}", sums);
}

Ausgabe:

15
[3, 7]
▶ Selbst ausprobieren

Der Wrapper leiht sich bonus aus dem umgebenden Code – genau dafür sind Closures gut. Eine Funktion direkt beim Namen zu übergeben (apply(double, 5)), klappt nur, wenn ihre Parameter schon passen. Siehe Closure-Traits.

In einer generischen Funktion. Ruft fn call_twice<F: Fn(i32)>(f: F) intern f(2, 3) auf, bekommst du E0057 mit note: callable defined here, und die Notiz zeigt auf Fn(i32). Der Bound ist die Parameterliste. Lösung: Ruf f(2) auf, oder ändere den Bound zu Fn(i32, i32), wenn du wirklich zwei Werte brauchst (dann muss das Closure jedes Aufrufers zwei nehmen).

Closures in einer Box. let f: Box<dyn Fn(i32, i32) -> i32> = Box::new(|a, b| a + b); und dann f(1) führt zu E0057 mit note: implementation defined here. Lösung: Übergib beide Werte, f(1, 2).

Sortieren. v.sort_by(|a| a.cmp(a)) führt zu E0593, „closure is expected to take 2 arguments, but it takes 1 argument“ (das Closure soll 2 Argumente nehmen, nimmt aber 1). sort_by vergleicht immer zwei Elemente. Lösung: v.sort_by(|a, b| a.cmp(b)). Siehe Sortieren.

Richtige Anzahl, falscher Typ. nums.iter().map(|x: i32| x * 2) führt zu E0631, „type mismatch in closure arguments“ (Typkonflikt bei den Closure-Argumenten), weil .iter() &i32 liefert. rustc schlägt |x: &i32| vor. Am einfachsten: Lass die Annotation weg, |x| x * 2.

Etwas auspacken, das kein Tupel ist. v.iter().map(|(a, b)| a + b) auf einem einfachen Vec<i32> führt zu E0308, „expected integer, found (_, _)“. Lösung: Nimm |(a, b)| nur, wenn die Elemente wirklich Paare sind.

Kurzer Check

1. let double = |x: i32| x * 2; wird als double(2, 3) aufgerufen. Was meldet rustc?

2. names.iter().zip(ages.iter()).map(|n, a| ...) kompiliert nicht. Was ist die Lösung?

3. E0057 und E0061 sagen beide this function takes 2 arguments but 1 argument was supplied. Woran erkennst du, dass du ein Closure aufgerufen hast?

Punkte: 0 / 3