Error Rust E0593: closure is expected to take a single 2-tuple as argument
E0593 artinya kamu memberikan sebuah closure (fungsi anonim) atau fungsi ke kode lain, misalnya map, sort_by, atau thread::spawn, dan jumlah parameternya tidak cocok dengan cara closure itu akan dipanggil. Kasus yang paling sering: iterator memberikan satu tuple ke closure-mu, tapi kamu menulis dua parameter. Biasanya perbaikannya cuma satu karakter, dan rustc sering sudah menuliskannya untukmu. Kalau justru kamu yang memanggil closure dengan jumlah nilai yang salah, itu E0057. Untuk error lain di seri ini, lihat Penjelasan error compiler Rust.
Pesan error-nya
Section titled “Pesan error-nya”error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments --> src/main.rs:6:10 |6 | .map(|i, name| format!("{}. {name}", i + 1)) // ❌ two parameters | ^^^ --------- 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 |6 - .map(|i, name| format!("{}. {name}", i + 1)) // ❌ two parameters6 + .map(|(i, name)| format!("{}. {name}", i + 1)) // ❌ two parameters |Compiler bilang map mengharapkan closure yang menerima satu tuple berisi dua nilai, tapi closure-mu menerima dua argumen terpisah. Ia juga langsung menyarankan perbaikannya: tambahkan tanda kurung, |(i, name)|.
Ini program yang menghasilkan error tersebut:
fn main() { let names = vec!["Ferris", "Corro", "Rusty"]; let lines: Vec<String> = names .iter() .enumerate() .map(|i, name| format!("{}. {name}", i + 1)) // ❌ two parameters .collect(); println!("{:?}", lines);}E0593 juga bisa muncul dengan beberapa baris pertama lain. Semuanya berarti “jumlah parameternya salah”:
error[E0593]: closure is expected to take 2 arguments, but it takes 1 argumenterror[E0593]: closure is expected to take 0 arguments, but it takes 1 argumenterror[E0593]: function is expected to take 1 argument, but it takes 2 argumentserror[E0593]: closure is expected to take 2 distinct arguments, but it takes a single 2-tuple as argumentBerturut-turut: diharapkan 2 argumen tapi closure-nya menerima 1; diharapkan 0 tapi menerima 1; sebuah fungsi diharapkan menerima 1 argumen tapi menerima 2; dan kebalikan dari contoh di atas, diharapkan 2 argumen terpisah tapi closure-nya menerima satu tuple.
Apa artinya
Section titled “Apa artinya”Lihat dua penanda di bawah baris 6:
^^^menunjuk kemap. Dialah yang memanggil closure-mu, dan dia yang menentukan berapa nilai yang diberikan.---------menunjuk ke daftar parameter|i, name|. Itulah yang kamu janjikan untuk diterima.
Keduanya tidak sepakat. enumerate() mengubah setiap item menjadi satu tuple, (index, item), dan map memberikan tuple itu ke closure-mu sebagai satu argumen. Closure-mu malah meminta dua argumen terpisah.
Bayangkan lubang surat di pintu yang hanya bisa menerima satu amplop setiap kali. Kamu boleh memasukkan dua lembar surat ke dalam amplop (sebuah tuple), tapi lubang itu tidak mau menerima dua lembar surat lepas. |(i, name)| artinya “berikan amplopnya, nanti aku buka di sini”.
Kata “closure” atau “function” di pesan error memberi tahu apa yang kamu berikan: closure yang ditulis langsung di tempat, atau fn bernama seperti .map(add).
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Setiap method yang menerima closure menyatakan dengan tepat bagaimana closure itu akan dipanggil. map menginginkan sesuatu seperti FnMut(Self::Item) -> B: satu argumen, yaitu item-nya. Untuk enumerate(), item-nya adalah (usize, &&str). Itu satu nilai yang kebetulan berupa tuple. sort_by menginginkan FnMut(&T, &T) -> Ordering: dua argumen. thread::spawn menginginkan FnOnce() -> T: tanpa argumen sama sekali.
Rust tidak pernah memecah tuple menjadi argumen terpisah untukmu, dan tidak pernah membungkus argumen terpisah menjadi tuple. Dengan begitu aturannya tetap sederhana: apa yang tertulis di signature method, itulah yang diterima closure-mu. Begitu kamu bisa membaca signature seperti ini, kamu bisa menebak apa yang diberikan oleh method iterator mana pun.
Kamu bisa membaca lebih lanjut tentang trait Fn di balik semua ini di Trait closure, dan tentang tuple di Destructuring tuple.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: Bongkar tuple-nya di daftar parameter
Section titled “Perbaikan 1: Bongkar tuple-nya di daftar parameter”Pilih ini kalau pesannya bilang “a single 2-tuple”. Inilah perbaikan yang paling sering kamu butuhkan. Tambahkan tanda kurung: |(i, name)| menerima satu tuple dan memberi nama pada kedua bagiannya. Cara ini bekerja untuk enumerate, zip, dan iterasi atas sebuah map:
use std::collections::BTreeMap;
fn main() { let names = vec!["Ferris", "Corro", "Rusty"];
// enumerate memberikan satu tuple (index, name) untuk setiap item let lines: Vec<String> = names .iter() .enumerate() .map(|(i, name)| format!("{}. {name}", i + 1)) .collect(); println!("{:?}", lines);
// iterasi atas map juga memberikan satu tuple (key, value) untuk setiap item let mut stock = BTreeMap::new(); stock.insert("apples", 3); stock.insert("pears", 0); stock.insert("plums", 5); let total: i32 = stock.iter().map(|(_name, count)| *count).sum(); println!("{total} buah tersedia");}Output:
["1. Ferris", "2. Corro", "3. Rusty"]8 buah tersediaTanda _ di awal _name memberi tahu Rust “aku tahu ini tidak kupakai”. Kamu juga bisa menulis _ saja. Ide yang sama untuk zip ditunjukkan di halaman E0057.
Perbaikan 2: Tulis parameter sebanyak yang diberikan method-nya
Section titled “Perbaikan 2: Tulis parameter sebanyak yang diberikan method-nya”Pilih ini kalau pesannya bilang “expected to take N arguments”. Cek apa yang diberikan method itu, lalu tulis parameter tepat sebanyak itu:
sort_bymembandingkan dua item:|a, b|.for_each,map,filtermemberikan satu item:|item|, atau|_|kalau kamu tidak membutuhkannya.thread::spawntidak memberikan apa-apa:||, biasanyamove ||.
use std::thread;
fn main() { // sort_by membandingkan dua item sekaligus: dua parameter let mut scores = vec![42, 7, 99, 15]; scores.sort_by(|a, b| b.cmp(a)); println!("{:?}", scores);
// for_each memberikan satu item: satu parameter (pakai _ kalau tidak dibutuhkan) let tasks = vec!["write", "test", "ship"]; tasks.iter().for_each(|task| println!("tugas: {task}")); tasks.iter().for_each(|_| print!("*")); println!();
// thread::spawn memanggil closure tanpa argumen: nol parameter let id = 1; let handle = thread::spawn(move || format!("worker {id} selesai")); println!("{}", handle.join().unwrap());}Output:
[99, 42, 15, 7]tugas: writetugas: testtugas: ship***worker 1 selesaiUntuk memberikan data ke sebuah thread, jangan tambahkan parameter. Tangkap datanya dengan move, seperti id di atas. Lihat Konkurensi dan Mengurutkan dan mencari.
Perbaikan 3: Bungkus fungsi bernama, atau buat fungsi yang menerima tuple
Section titled “Perbaikan 3: Bungkus fungsi bernama, atau buat fungsi yang menerima tuple”Pilih ini kalau pesannya bilang “function is expected to take…”. Kamu memberikan fungsi lewat namanya, seperti .map(add), dan parameternya tidak cocok. Di sini item-nya berupa pasangan, tapi add menerima dua angka:
error[E0593]: function is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments --> src/main.rs:7:48 |1 | fn add(a: i32, b: i32) -> i32 { | ----------------------------- takes 2 distinct arguments...7 | let sums: Vec<i32> = pairs.into_iter().map(add).collect(); | --- ^^^ expected function that takes a single 2-tuple as argument | | | required by a bound introduced by this callRustc menunjuk ke add (menerima dua argumen terpisah) dan ke map (yang akan memanggilnya dengan satu tuple).
Kamu bisa membungkus fungsi itu dalam closure kecil yang membongkar tuple lalu memanggilnya, atau menulis versi yang langsung menerima tuple:
fn add(a: i32, b: i32) -> i32 { a + b}
// versi yang menerima pasangan sebagai satu tuplefn add_pair((a, b): (i32, i32)) -> i32 { a + b}
fn main() { let pairs = vec![(1, 2), (3, 4), (10, 20)];
// bungkus: satu tuple masuk, dua argumen keluar let sums: Vec<i32> = pairs.iter().map(|&(a, b)| add(a, b)).collect(); println!("{:?}", sums);
// atau berikan fungsi yang memang sudah menerima satu tuple let sums2: Vec<i32> = pairs.into_iter().map(add_pair).collect(); println!("{:?}", sums2);}Output:
[3, 7, 30][3, 7, 30]Tanda & di |&(a, b)| ada karena .iter() meminjamkan setiap pasangan sebagai &(i32, i32), dan pattern itu menyalin kedua angkanya keluar. Pembungkus biasanya pilihan yang lebih baik: add tetap fungsi dua argumen biasa yang bisa dipanggil oleh bagian kode lain.
Kalau fungsi generik buatanmu sendiri meminta Fn(i32) -> i32 dan kamu memberikan add yang menerima dua argumen, kamu akan mendapat “function is expected to take 1 argument, but it takes 2 arguments”. Perbaikannya adalah pembungkus yang mengisi nilai tambahannya, seperti di Perbaikan 3 halaman E0057.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”HashMap::retain (kebalikannya). Kebanyakan method map memberimu sebuah tuple, tapi retain memberikan key dan value sebagai dua argumen terpisah. Jadi stock.retain(|(name, count)| *count > 0) memberi “closure is expected to take 2 distinct arguments, but it takes a single 2-tuple as argument”. Perbaikannya: hapus tanda kurungnya, stock.retain(|_name, count| *count > 0). Vec::retain menerima satu item, seperti biasa.
Lupa menulis parameter. tasks.iter().for_each(|| println!("task")) memberi “closure is expected to take 1 argument, but it takes 0 arguments”, dan rustc menyarankan |_|. Perbaikannya: |_|, atau |task| kalau kamu ingin memakainya.
Thread. thread::spawn(|id| ...) memberi “closure is expected to take 0 arguments, but it takes 1 argument”. Perbaikannya: thread::spawn(move || ...) dan tangkap id dari luar.
Parameter function pointer. Kalau fungsimu menerima f: fn(i32) -> i32 (function pointer biasa, bukan generik F: Fn), memberikan add yang menerima dua argumen malah menghasilkan E0308, dengan label “incorrect number of function parameters” (jumlah parameter fungsi salah). Perbaikannya sama: closure pembungkus seperti |x| add(x, 10). Closure yang tidak menangkap apa pun bisa diberikan sebagai pointer fn.
Error terkait
Section titled “Error terkait”- E0057: closure takes N arguments but M were supplied: kamu sendiri yang memanggil closure dengan jumlah nilai yang salah.
- E0631: type mismatch in closure arguments: jumlahnya benar, tapi sebuah parameter bertipe salah, sering
i32padahal iterator memberikan&i32. - E0061: function takes N arguments but M were supplied: memanggil fungsi biasa dengan jumlah argumen yang salah.
- E0308: mismatched types: yang kamu dapat dengan parameter pointer
fn(...), atau dengan|(a, b)|padahal item-nya sama sekali bukan tuple. - E0599: no method found: kadang muncul tepat setelah E0593, misalnya “the method
collectexists … but its trait bounds were not satisfied”. Perbaiki E0593 dulu, maka error ini ikut hilang. - Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. names.iter().enumerate().map(|i, name| ...) memberi E0593. Apa perbaikannya?
2. Berapa parameter yang dibutuhkan closure untuk scores.sort_by(...)?
3. thread::spawn(|id| println!("{id}")) memberi E0593. Bagaimana caranya memasukkan id ke dalam thread?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0593 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini.
- Closures — The Rust Book (bahasa Inggris): sintaks closure, bagaimana tipe parameter disimpulkan, dan trait
Fn. - As input parameters — Rust by Example (bahasa Inggris): bagaimana bound
Fnpada sebuah fungsi menentukan closure mana yang diterimanya. Iterator::enumerate— dokumentasi standard library (bahasa Inggris): menunjukkan bahwa setiap item menjadi satu tuple(index, value).HashMap::retain— dokumentasi standard library (bahasa Inggris): method map yang closure-nya menerima key dan value sebagai dua argumen.