Lewati ke konten

Error Rust E0592: duplicate definitions with name X (method ganda)

E0592 artinya: satu tipe yang sama punya dua method, associated function, atau konstanta dengan nama yang sama, yang ditulis di blok impl. Sebuah tipe boleh punya blok impl sebanyak yang kamu mau, tapi semuanya berbagi satu daftar nama, jadi setiap nama hanya boleh dipakai sekali. Biasanya ini akibat copy-paste, merge, atau blok impl kedua di file lain. Perbaikannya: simpan satu, atau beri nama baru untuk yang lain. (Halaman ini bagian dari Penjelasan error compiler Rust.)

error[E0592]: duplicate definitions with name `reset`
--> src/main.rs:10:5
|
10 | fn reset(&mut self) {
| ^^^^^^^^^^^^^^^^^^^ duplicate definitions for `reset`
...
16 | fn reset(&mut self) { // ❌ `reset` sudah didefinisikan di atas
| ------------------- other definition for `reset`

Artinya: ada definisi ganda dengan nama reset. rustc menandai definisi yang satu dengan ^^^ dan “definisi lainnya” dengan ---.

Ini program yang menghasilkan error tersebut:

struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Counter { count: 0 }
}
fn reset(&mut self) {
self.count = 0;
}
}
impl Counter {
fn reset(&mut self) { // ❌ `reset` sudah didefinisikan di atas
self.count = 0;
}
}
fn main() {
let mut c = Counter::new();
c.reset();
println!("{}", c.count);
}

rustc juga melaporkan E0034, “multiple applicable items in scope” (ada beberapa item yang cocok di scope ini), di pemanggilan c.reset(), karena ia tidak tahu reset mana yang kamu maksud. Itu hanya error susulan: perbaiki E0592, dan E0034 ikut hilang.

Judul error-nya menyebut nama yang didefinisikan dua kali (reset). Tanda ^^^ menandai satu definisi, dan baris --- menandai “other definition” (definisi lainnya). rustc menunjukkan nomor baris keduanya, jadi kamu bisa langsung melompat ke sana. Kalau keduanya berjauhan, tanda ... di antaranya hanya berarti “beberapa baris dilewati”.

Tidak masalah bahwa kedua reset itu ada di blok impl Counter yang berbeda, atau bahkan di file yang berbeda di crate-mu. Bagi Rust, semuanya adalah method dari tipe yang sama, jadi Counter::reset harus menunjuk ke tepat satu fungsi. Aturan yang sama berlaku untuk associated constant: dua const MAX: u32 untuk satu tipe juga E0592.

Ibaratnya daftar kontak di HP dengan dua entri bernama “Ibu”. Saat kamu bilang “telepon Ibu”, nomor mana yang harus dihubungi?

Memecah method ke beberapa blok impl itu boleh dan sering berguna: satu blok untuk constructor, satu untuk getter, satu di balik #[cfg(test)]. Lihat Menambahkan perilaku dengan method. Tapi saat kamu memanggil c.reset() atau Counter::reset(&mut c), Rust harus menemukan satu fungsi. Kalau ada dua, fungsi mana yang berjalan akan bergantung pada detail yang tidak terlihat oleh siapa pun yang membaca pemanggilannya.

Jadi Rust menolaknya di definisinya, bukan cuma di pemanggilannya. Ini menangkap bug yang nyata: dengan method hasil copy-paste, bisa saja kamu memperbaiki masalah di satu salinan, sementara salinan lainnya tetap berjalan. Rust memastikan hanya ada satu versi yang perlu diperbaiki.

Perbaikan 1: Hapus salinannya dan simpan satu definisi

Section titled “Perbaikan 1: Hapus salinannya dan simpan satu definisi”

Pilih ini hampir setiap saat. Kalau kedua salinan mengerjakan hal yang sama (copy-paste, merge yang berantakan, method yang kamu pindahkan tapi lupa dihapus), hapus salah satunya. Sebelum menghapus, bandingkan isi keduanya: kalau salah satunya punya perbaikan yang tidak ada di yang lain, simpan yang itu. Menggabungkan blok-blok impl menjadi satu juga membuat duplikat lebih mudah terlihat di kemudian hari.

struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Counter { count: 0 }
}
fn increment(&mut self) {
self.count += 1;
}
fn reset(&mut self) { // ✅ hanya satu `reset` untuk `Counter`
self.count = 0;
}
}
fn main() {
let mut c = Counter::new();
c.increment();
c.increment();
println!("sebelum reset: {}", c.count);
c.reset();
println!("setelah reset: {}", c.count);
}

Output:

sebelum reset: 2
setelah reset: 0
▶ Coba sendiri

Kalau kamu tidak bisa menemukan definisi keduanya, cari di seluruh proyek: grep -rn "fn reset" src/. Sebuah blok impl Counter bisa berada di modul mana pun di crate-mu.

Pilih ini kalau kedua definisi itu mengerjakan hal yang berbeda. Rust tidak punya overloading: kamu tidak bisa punya dua method dengan nama yang sama dan parameter yang berbeda, seperti reset() dan reset(start: u32). Itu juga E0592. Beri masing-masing nama yang menjelaskan apa yang dikerjakannya:

struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Counter { count: 0 }
}
fn reset(&mut self) {
self.count = 0;
}
}
impl Counter {
fn reset_to(&mut self, start: u32) { // ✅ nama berbeda untuk perilaku berbeda
self.count = start;
}
}
fn main() {
let mut c = Counter::new();
c.reset_to(10);
println!("setelah reset_to(10): {}", c.count);
c.reset();
println!("setelah reset(): {}", c.count);
}

Output:

setelah reset_to(10): 10
setelah reset(): 0
▶ Coba sendiri

Pola penamaan yang umum di Rust membantu di sini: new dan with_capacity, from_str dan from_bytes, get dan get_mut. Kalau kamu benar-benar ingin satu nama untuk beberapa tipe argumen, pakai parameter generic atau trait, seperti fn reset_to(&mut self, start: impl Into<u32>). Lihat Generics.

Perbaikan 3: Cegah blok impl generic tumpang tindih

Section titled “Perbaikan 3: Cegah blok impl generic tumpang tindih”

Pilih ini kalau salah satu dari kedua definisi ada di blok generic seperti impl<T> Wrapper<T>. Blok itu juga mencakup Wrapper<i32>, jadi impl Wrapper<i32> kedua dengan nama method yang sama adalah duplikat:

error[E0592]: duplicate definitions with name `describe`
--> src/main.rs:4:5
|
4 | fn describe(&self) -> String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `describe`
...
10 | fn describe(&self) -> String {
| ---------------------------- other definition for `describe`

Artinya: describe didefinisikan dua kali untuk Wrapper<i32>, sekali lewat blok generic dan sekali lewat blok khusus i32.

Program di baliknya:

struct Wrapper<T>(T);
impl<T> Wrapper<T> {
fn describe(&self) -> String {
String::from("sesuatu")
}
}
impl Wrapper<i32> {
fn describe(&self) -> String {
format!("angka {}", self.0)
}
}
fn main() {
println!("{}", Wrapper(5).describe());
}

Rust tidak mengizinkan impl yang spesifik menimpa impl yang generic (fitur itu, yang disebut specialization, belum stabil). Ada dua jalan keluar yang bagus: tulis blok impl hanya untuk tipe-tipe spesifik, supaya tidak bisa tumpang tindih, atau taruh perilakunya di sebuah trait dan implementasikan sekali untuk setiap tipe:

struct Wrapper<T>(T);
// Opsi A: blok impl untuk tipe spesifik tidak pernah tumpang tindih
impl Wrapper<i32> {
fn describe(&self) -> String {
format!("angka {}", self.0)
}
}
impl Wrapper<&str> {
fn describe(&self) -> String {
format!("teks \"{}\"", self.0)
}
}
// Opsi B: sebuah trait, diimplementasikan sekali untuk setiap tipe
trait Label {
fn label(&self) -> String;
}
impl Label for i32 {
fn label(&self) -> String {
format!("int {}", self)
}
}
impl Label for bool {
fn label(&self) -> String {
format!("flag {}", self)
}
}
impl<T: Label> Wrapper<T> {
fn label(&self) -> String {
self.0.label() // satu method generic, bagian per tipe ada di trait
}
}
fn main() {
println!("{}", Wrapper(5).describe());
println!("{}", Wrapper("hai").describe());
println!("{}", Wrapper(7).label());
println!("{}", Wrapper(true).label());
}

Output:

angka 5
teks "hai"
int 7
flag true
▶ Coba sendiri

Versi trait biasanya desain jangka panjang yang lebih baik: menambahkan dukungan untuk tipe baru cukup dengan satu impl Label for ... lagi. Lihat Trait.

Helper test di blok impl kedua. Kamu menambahkan impl Counter { fn new() -> Self { ... } } di dalam #[cfg(test)] mod tests untuk membuat nilai test, padahal Counter::new sudah ada. Blok test itu tetap sebuah impl Counter. Perbaikannya: beri nama seperti fn for_test(), atau pakai new yang asli. Lihat Testing.

Setelah merge atau refactor. Dua branch sama-sama menambahkan fn len(&self), atau sebuah method dipindahkan ke file baru dan salinan lamanya tertinggal. rustc menunjukkan kedua nomor barisnya; simpan yang lebih bagus.

Method trait dengan nama yang sama itu tidak masalah. impl Counter { fn reset(&mut self) } bersama impl Resettable for Counter { fn reset(&mut self) } bukan E0592: yang satu adalah method milik tipe itu sendiri, yang lain milik trait, dan c.reset() memanggil method milik tipe itu sendiri. Dua trait dengan nama method yang sama juga bisa dikompilasi, tapi memanggilnya dengan c.reset() memberi E0034; perbaiki dengan Resettable::reset(&mut c).

Kuis singkat

1. Sebuah tipe punya dua blok impl. Bisakah keduanya berisi method bernama reset?

2. Kamu ingin area() dan area(scale: f64). Apa yang kamu lakukan di Rust?

3. Kenapa impl<T> Wrapper<T> dan impl Wrapper<i32> bentrok kalau keduanya mendefinisikan describe?

Skor: 0 / 3