Lewati ke konten

Error Rust E0061: this function takes N arguments but M were supplied

E0061 artinya kamu memanggil sebuah fungsi dengan jumlah argumen yang salah: terlalu sedikit atau terlalu banyak. Ini salah satu error paling ramah di Rust, karena rustc memberitahumu dengan tepat argumen mana yang kurang atau berlebih, dan apa tipenya. Error ini sering muncul pada orang yang datang dari Python, JavaScript, atau C#, di mana argumen default dan overloading adalah hal biasa. Rust tidak punya keduanya, dan halaman ini menunjukkan apa yang bisa kamu lakukan sebagai gantinya. Untuk error lain di seri ini, lihat Penjelasan error compiler Rust.

error[E0061]: this function takes 2 arguments but 1 argument was supplied
--> src/main.rs:6:5
|
6 | greet("Ferris"); // ❌ greet butuh dua argumen
| ^^^^^---------- argument #2 of type `&str` is missing
|
note: function defined here
--> src/main.rs:1:4
|
1 | fn greet(name: &str, greeting: &str) {
| ^^^^^ --------------
help: provide the argument
|
6 | greet("Ferris", /* &str */); // ❌ greet butuh dua argumen
| ++++++++++++

Ini program yang menghasilkan error tersebut:

fn greet(name: &str, greeting: &str) {
println!("{greeting}, {name}!");
}
fn main() {
greet("Ferris"); // ❌ greet butuh dua argumen
}

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[E0061]: this function takes 2 arguments but 3 arguments were supplied
--> src/main.rs:6:13
|
6 | let a = area(3, 4, 5);
| ^^^^ - unexpected argument #3 of type `{integer}`

Untuk method yang dipanggil dengan titik, baris pertamanya berbunyi “this method takes…” (method ini menerima…). Untuk tuple struct seperti Rgb(255, 0), bunyinya “this struct takes 3 arguments”. Error-nya tetap sama.

Pesannya punya tiga bagian, dan masing-masing membantu:

  1. Baris pertama menghitung: fungsinya butuh 2 argumen, kamu memberi 1.
  2. Label di bawah pemanggilanmu menyebut masalahnya: “argument #2 of type &str is missing” (argumen ke-2 bertipe &str tidak ada).
  3. note: melompat ke definisi fungsinya dan menggarisbawahi parameter yang kamu lewatkan, greeting: &str.

Baris help: menunjukkan di mana argumennya harus ditaruh. /* &str */ hanyalah penanda yang memberitahu tipenya; itu komentar, jadi kalau kamu menempelkannya apa adanya, kodenya tidak akan bisa dikompilasi. Isi dengan nilai sungguhan, seperti "Halo".

Ini seperti mesin kopi yang butuh gelas dan koin sekaligus. Memasukkan koinnya saja tidak memberimu setengah gelas kopi. Mesinnya sama sekali tidak menyala.

Di Rust, signature fungsi adalah kontrak yang persis: parameter-parameter ini, dengan urutan ini, dengan tipe-tipe ini. Setiap pemanggilan harus cocok dengannya. Tidak ada argumen default (greeting = "Hello"), tidak ada overloading (dua fungsi bernama greet dengan parameter berbeda), dan tidak ada fungsi yang menerima argumen “berapa pun jumlahnya”. Mencoba menulis nilai default memberi error: parameter defaults are not supported, dan greet kedua memberi E0428, “the name greet is defined multiple times” (nama greet didefinisikan lebih dari sekali).

Kedengarannya ketat, tapi ada manfaatnya. Saat kamu membaca greet(name, greeting), kamu tahu persis fungsi mana yang berjalan dan apa yang diterimanya. Saat kamu menambahkan parameter ke sebuah fungsi, compiler mendaftar setiap pemanggilan yang perlu diperbarui, jadi tidak ada yang terlewat. (Hal seperti println! yang memang menerima nilai berapa pun adalah makro, dan itulah arti tanda !. Lihat Makro.)

Cara kerja parameter dibahas di Fungsi.

Perbaikan 1: Berikan persis apa yang diminta signature

Section titled “Perbaikan 1: Berikan persis apa yang diminta signature”

Pilih ini hampir setiap saat. Buka lokasi note:, baca daftar parameternya, dan berikan satu nilai per parameter, sesuai urutan:

fn greet(name: &str, greeting: &str) {
println!("{greeting}, {name}!");
}
fn main() {
greet("Ferris", "Halo");
greet("Corro", "Selamat pagi");
}

Output:

Halo, Ferris!
Selamat pagi, Corro!
▶ Coba sendiri

Kalau kamu baru saja mengubah fungsinya dan sekarang semua pemanggilan rusak, itu tandanya compiler sedang membantumu: perbaiki setiap pemanggilan yang didaftarnya, atau tanyakan pada dirimu apakah parameter baru itu sebaiknya opsional (Perbaikan 3).

Pilih ini kalau fungsinya punya parameter self. Method biasanya dipanggil sebagai value.method(args). Nilai sebelum titik mengisi self, jadi tidak ikut dihitung. Kalau kamu menulis Type::method(args), tidak ada yang mengisi self:

error[E0061]: this function takes 2 arguments but 1 argument was supplied
--> src/main.rs:13:5
|
13 | Account::deposit(50);
| ^^^^^^^^^^^^^^^^ -- argument #1 of type `&mut Account` is missing
|
help: provide the argument
|
13 | Account::deposit(/* &mut Account */, 50);
| +++++++++++++++++++

Perhatikan cara menghitungnya. Saat dipanggil sebagai Account::deposit(...), rustc bilang fungsi itu menerima 2 argumen, karena &mut self ikut dihitung. Saat dipanggil sebagai acc.deposit(), rustc bilang method itu menerima 1 argumen, karena titiknya sudah mengisi self. Kedua bentuk berhasil asalkan semua argumennya ada:

struct Account {
balance: i64,
}
impl Account {
fn deposit(&mut self, amount: i64) {
self.balance += amount;
}
}
fn main() {
let mut acc = Account { balance: 0 };
acc.deposit(50); // cara yang biasa
Account::deposit(&mut acc, 25); // pemanggilan yang sama, ditulis lengkap
println!("Saldo: {}", acc.balance);
}

Output:

Saldo: 75
▶ Coba sendiri

Pakai bentuk titik di kode sehari-hari. Type::method(&x, a) berguna kalau kamu perlu memberikan method itu sendiri ke tempat lain, seperti team.iter().map(Player::label) (contoh). Kalau sebuah fungsi sama sekali tidak punya self (seperti new), ia hanya bisa dipanggil sebagai Type::function(...). Lihat Struct dan E0424.

Perbaikan 3: Pakai Option<T> untuk argumen yang kadang tidak diperlukan

Section titled “Perbaikan 3: Pakai Option<T> untuk argumen yang kadang tidak diperlukan”

Pilih ini kalau satu argumen biasanya punya nilai default yang masuk akal. Jadikan Option, berikan None untuk “pakai default”, dan isi dengan unwrap_or:

fn connect(host: &str, port: u16, timeout_secs: Option<u64>) {
let timeout = timeout_secs.unwrap_or(30);
println!("Menghubungkan ke {host}:{port} (timeout {timeout} detik)");
}
fn main() {
connect("localhost", 8080, None);
connect("example.com", 443, Some(5));
}

Output:

Menghubungkan ke localhost:8080 (timeout 30 detik)
Menghubungkan ke example.com:443 (timeout 5 detik)
▶ Coba sendiri

Jujur saja: pemanggil tetap menulis argumennya, yaitu None. Rust lebih suka begitu, karena pemanggilannya menunjukkan bahwa nilai default sedang dipakai. Kalau kamu lebih suka tidak menulisnya sama sekali, buat dua fungsi dengan nama yang jelas, seperti connect(host, port) yang memanggil connect_with_timeout(host, port, 30). Itulah jawaban Rust yang biasa untuk overloading. Lihat Option.

Perbaikan 4: Pakai struct config dengan Default

Section titled “Perbaikan 4: Pakai struct config dengan Default”

Pilih ini kalau sebuah fungsi punya banyak pengaturan dan kebanyakan pemanggil hanya mengubah satu atau dua. Taruh pengaturannya di sebuah struct, implementasikan Default, dan biarkan pemanggil mengisi hanya yang mereka perlukan dengan ..Default::default():

struct ServerConfig {
host: String,
port: u16,
timeout_secs: u64,
verbose: bool,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
host: String::from("localhost"),
port: 8080,
timeout_secs: 30,
verbose: false,
}
}
}
fn start(config: ServerConfig) {
println!(
"Server berjalan di {}:{} (timeout {} detik, verbose: {})",
config.host, config.port, config.timeout_secs, config.verbose
);
}
fn main() {
start(ServerConfig::default());
start(ServerConfig { port: 3000, verbose: true, ..Default::default() });
}

Output:

Server berjalan di localhost:8080 (timeout 30 detik, verbose: false)
Server berjalan di localhost:3000 (timeout 30 detik, verbose: true)
▶ Coba sendiri

Cara ini juga memberimu sesuatu yang mirip argumen bernama: port: 3000 menjelaskan arti angka itu. Kalau default setiap field sama dengan default tipenya sendiri (0, false, string kosong), #[derive(Default)] menuliskan impl-nya untukmu. Untuk API yang lebih besar, builder pattern (Server::builder().port(3000).build()) adalah langkah berikutnya. Lihat Struct dan Design pattern.

Setelah menambahkan parameter. Kamu memberi fn save(path: &str) parameter kedua, dan semua pemanggilan lama save("notes.txt") rusak dengan E0061. Perbaikan: perbarui setiap pemanggilan yang didaftar compiler, atau pakai Perbaikan 3 atau Perbaikan 4 kalau nilai barunya biasanya sama.

Method standard library. names.push("Cy", "Di") memberi “this method takes 1 argument but 2 arguments were supplied”. push menambahkan satu item. Perbaikan: panggil dua kali, atau pakai names.extend(["Cy", "Di"]). Lihat Vektor.

Tuple struct. struct Rgb(u8, u8, u8); lalu Rgb(255, 0) memberi “this struct takes 3 arguments but 2 arguments were supplied”. Perbaikan: isi setiap field, Rgb(255, 0, 0).

Kebiasaan dari bahasa lain. greet("Ferris", greeting="Hi") ala Python bukan sintaks Rust. Menulis greet(greeting = "Hi") tidak memberi nama pada argumen; rustc membacanya sebagai assignment ke variabel bernama greeting dan juga melaporkan E0425. Perbaikan: berikan nilai berdasarkan posisi, atau pakai struct config (Perbaikan 4).

Closure. Memanggil add(1) padahal add adalah |a: i32, b: i32| a + b memberi pesan yang sama, tapi dengan kode E0057 (versi closure). Perbaikannya sama: berikan kedua nilai.

  • E0308: jumlah argumennya benar, tapi salah satunya bertipe salah, sering karena dua argumen tertukar.
  • E0424: self dipakai di fungsi yang tidak punya parameter self, sisi lain dari method vs. associated function.
  • E0599: tidak ada method dengan nama itu, misalnya memanggil associated function seperti new dengan titik.
  • E0425: nilai tidak ditemukan, bisa muncul bersama E0061 saat kamu mencoba argumen bernama.
  • Semua error compiler: daftar lengkap halaman error. Juga terkait: E0057 (closure dipanggil dengan jumlah argumen yang salah) dan E0428 (fungsi didefinisikan dua kali saat mencoba overloading), yang tidak punya halaman sendiri.

Kuis singkat

1. fn area(w: u32, h: u32) -> u32 dipanggil sebagai area(5). Apa yang dilaporkan rustc?

2. Sebuah method ditulis fn deposit(&mut self, amount: i64). Pemanggilan mana yang benar?

3. Kamu ingin argumen timeout yang tidak dipedulikan kebanyakan pemanggil. Apa pendekatan yang umum di Rust?

Skor: 0 / 3