Lewati ke konten

Error Rust E0057: this function takes N arguments but M were supplied (closure)

E0057 artinya kamu memanggil sebuah closure (fungsi anonim) dengan jumlah argumen yang salah: terlalu sedikit atau terlalu banyak. Ini versi closure dari E0061, dan pesannya hampir sama persis. Perbaikannya biasanya cepat: lihat |...| milik closure itu, lalu berikan satu nilai untuk setiap parameter. Halaman ini juga membahas dua kerabat dekatnya yang akan kamu temui saat memakai iterator, E0593 dan E0631, karena keduanya berasal dari kekeliruan yang sama. Untuk error lain di seri ini, lihat Penjelasan error compiler Rust.

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

Compiler bilang closure ini butuh 2 argumen tapi hanya diberi 1, argumen ke-2 bertipe i32 belum ada, dan ia menunjukkan di mana closure itu didefinisikan.

Ini program yang menghasilkan error tersebut:

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

Argumen yang terlalu banyak memberi kode yang sama, dengan label “unexpected argument” (argumen tak terduga) dan bantuan “remove the extra argument” (hapus argumen yang berlebih):

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

Baca pesannya seperti pesan untuk fungsi biasa: baris pertama menghitung (closure-nya menerima 2, kamu memberi 1), label di bawah pemanggilanmu menyebut argumen mana yang kurang atau berlebih, dan help: menunjukkan tempatnya. /* i32 */ hanyalah komentar penanda yang memberitahu tipenya. Isi dengan nilai sungguhan.

E0057 atau E0061? Baris pertamanya sama. Yang membedakan adalah note:-nya:

  • closure defined here: kamu memanggil closure. Itu E0057.
  • callable defined here: kamu memanggil f generik dengan bound seperti F: Fn(i32). Ini juga E0057.
  • function defined here: fn biasa, method, atau function pointer (let f: fn(i32, i32) -> i32 = add;). Itu E0061.

Apa pun kasusnya, perbaikannya sama: samakan pemanggilan dengan daftar parameternya.

Daftar parameter closure sama tetapnya dengan daftar parameter fungsi. |a: i32, b: i32| menerima tepat dua i32, setiap kali. Tidak ada argumen default dan tidak ada “boleh kasih lebih sedikit kalau mau”. Bahkan kalau kamu tidak menulis tipenya, rustc menyimpulkannya dari isi closure dan pemakaian pertamanya, dan sejak itu tipenya terkunci.

Di balik layar, closure mengimplementasikan salah satu trait Fn dengan daftar tipe argumen yang tetap, seperti Fn(i32, i32) -> i32. Compiler memeriksa setiap pemanggilan terhadap daftar itu, jadi pemanggilan dengan jumlah argumen yang salah tidak akan pernah sampai ke program yang berjalan. Cara kerja closure dan parameternya dibahas di Closure dan Trait closure.

Perbaikan 1: Samakan pemanggilan dengan parameter closure

Section titled “Perbaikan 1: Samakan pemanggilan dengan parameter closure”

Pilih ini hampir setiap saat. Lompat ke lokasi note:, hitung parameter di antara tanda pipa |...|, dan berikan satu nilai untuk masing-masing. Kalau nilaimu lebih banyak daripada parameternya, panggil closure itu lebih dari sekali:

fn main() {
let add = |a: i32, b: i32| a + b;
let greet = |name: &str| println!("Halo, {name}!");
let total = add(1, 2);
println!("{total}");
greet("Ferris");
greet("Corro"); // satu pemanggilan per nama
}

Output:

3
Halo, Ferris!
Halo, Corro!
▶ Coba sendiri

Kalau closure-nya memang seharusnya menerima jumlah nilai yang berbeda, ubah closure-nya saja. Itu juga tidak masalah, asalkan setiap pemanggilan sesuai dengannya.

Perbaikan 2: Tuple: satu tuple adalah satu argumen

Section titled “Perbaikan 2: Tuple: satu tuple adalah satu argumen”

Pilih ini kalau ada tuple yang terlibat. |(name, score)| dan |name, score| kelihatan mirip, tapi keduanya closure yang berbeda. Yang pertama menerima satu argumen, yaitu sebuah tuple, lalu membongkarnya. Yang kedua menerima dua argumen.

Memanggil closure tuple dengan dua nilai memberi E0057, dan rustc menyarankan untuk menambahkan tanda kurung:

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));
| + +

Kesalahan sebaliknya sering sekali terjadi dengan iterator. zip, enumerate, dan iterasi atas map memberi closure-mu satu tuple per item. Menulis .map(|name, score| ...) memberi kode yang berbeda, 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}"))

Compiler bilang closure itu seharusnya menerima satu tuple berisi 2 elemen, tapi kamu menulis 2 argumen terpisah. Keduanya punya perbaikan yang sama: taruh tanda kurung di tempat tuple-nya berada.

fn main() {
let names = vec!["Ferris", "Corro"];
let scores = vec![90, 75];
// zip memberi satu tuple (name, score) per item: bongkar di daftar parameter
let lines: Vec<String> = names
.iter()
.zip(scores.iter())
.map(|(name, score)| format!("{name}: {score}"))
.collect();
println!("{:?}", lines);
// closure yang menerima tuple dipanggil dengan satu tuple
let show = |(name, score): (&str, i32)| println!("{name} mendapat nilai {score}");
show(("Ferris", 90));
}

Output:

["Ferris: 90", "Corro: 75"]
Ferris mendapat nilai 90
▶ Coba sendiri

Hal yang sama berlaku untuk .enumerate().map(|(i, item)| ...) dan map.iter().for_each(|(key, value)| ...). Lihat Iterator dan Tuple.

Perbaikan 3: Sesuaikan dengan closure pembungkus

Section titled “Perbaikan 3: Sesuaikan dengan closure pembungkus”

Pilih ini kalau kamu memberikan sebuah fungsi atau closure ke fungsi lain (sebuah “higher-order function”) dan jumlah parameternya tidak cocok. Di sini apply menginginkan sesuatu yang menerima satu i32, tapi add menerima dua. Hasilnya E0593 lagi:

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

Compiler bilang apply mengharapkan fungsi yang menerima 1 argumen, tapi add menerima 2. Jangan ubah add. Bungkus dengan closure kecil yang bentuknya sesuai harapan apply, dan isi sendiri nilai yang kurang:

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;
// menerima 1 argumen, memanggil add dengan 2
println!("{}", apply(|x| add(x, bonus), 5));
// menerima 1 tuple, memanggil add dengan kedua bagiannya
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]
▶ Coba sendiri

Closure pembungkus itu meminjam bonus dari kode di sekitarnya, dan itulah keahlian closure. Memberikan fungsi lewat namanya (apply(double, 5)) hanya berhasil kalau parameternya sudah cocok. Lihat Trait closure.

Di dalam fungsi generik. fn call_twice<F: Fn(i32)>(f: F) yang memanggil f(2, 3) memberi E0057 dengan note: callable defined here, menunjuk ke Fn(i32). Bound itulah daftar parameternya. Perbaikan: panggil f(2), atau ubah bound-nya menjadi Fn(i32, i32) kalau kamu memang butuh dua nilai (maka closure dari setiap pemanggil harus menerima dua).

Closure dalam Box. let f: Box<dyn Fn(i32, i32) -> i32> = Box::new(|a, b| a + b); lalu f(1) memberi E0057 dengan note: implementation defined here. Perbaikan: berikan kedua nilai, f(1, 2).

Mengurutkan. v.sort_by(|a| a.cmp(a)) memberi E0593, “closure is expected to take 2 arguments, but it takes 1 argument”. sort_by membandingkan dua item sekaligus. Perbaikan: v.sort_by(|a, b| a.cmp(b)). Lihat Mengurutkan dan mencari.

Jumlahnya benar, tipenya salah. nums.iter().map(|x: i32| x * 2) memberi E0631, “type mismatch in closure arguments” (tipe argumen closure tidak cocok), karena .iter() memberikan &i32. Bantuan dari rustc menyarankan |x: &i32|. Perbaikan paling sederhana: hapus anotasinya, |x| x * 2.

Membongkar sesuatu yang bukan tuple. v.iter().map(|(a, b)| a + b) pada Vec<i32> biasa memberi E0308, “expected integer, found (_, _)”. Perbaikan: pakai |(a, b)| hanya kalau item-nya memang berpasangan.

Kuis singkat

1. let double = |x: i32| x * 2; dipanggil sebagai double(2, 3). Apa yang dilaporkan rustc?

2. names.iter().zip(ages.iter()).map(|n, a| ...) tidak bisa dikompilasi. Apa perbaikannya?

3. E0057 dan E0061 sama-sama bilang this function takes 2 arguments but 1 argument was supplied. Bagaimana kamu tahu yang kamu panggil adalah closure?

Skor: 0 / 3