Error Rust E0034: multiple applicable items in scope (method ambigu)
E0034 artinya: tipemu punya dua method dengan nama yang sama, masing-masing dari dua trait yang berbeda, dan kamu memanggilnya dengan cara singkat, seperti book.describe(). Rust melihat keduanya dan tidak mau memilihkan salah satu untukmu. Tidak ada yang rusak pada tipe-tipemu; kamu cukup menyebutkan trait mana yang kamu maksud. Biasanya itu cuma butuh satu baris, dan rustc bahkan menuliskannya untukmu. (Halaman ini bagian dari Penjelasan error compiler Rust.)
Pesan error-nya
Section titled “Pesan error-nya”error[E0034]: multiple applicable items in scope --> src/main.rs:28:25 |28 | println!("{}", book.describe()); // ❌ milik Summary atau milik Label? | ^^^^^^^^ multiple `describe` found |note: candidate #1 is defined in an impl of the trait `Label` for the type `Book` --> src/main.rs:21:5 |21 | fn describe(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^note: candidate #2 is defined in an impl of the trait `Summary` for the type `Book` --> src/main.rs:15:5 |15 | fn describe(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^help: disambiguate the method for candidate #1 |28 - println!("{}", book.describe()); // ❌ milik Summary atau milik Label?28 + println!("{}", Label::describe(&book)); // ❌ milik Summary atau milik Label? |help: disambiguate the method for candidate #2 |28 - println!("{}", book.describe()); // ❌ milik Summary atau milik Label?28 + println!("{}", Summary::describe(&book)); // ❌ milik Summary atau milik Label? |Artinya: ada lebih dari satu item dengan nama itu yang bisa dipakai di cakupan (scope) ini. rustc menunjukkan kedua kandidatnya, lalu cara memanggil masing-masing dengan jelas.
Ini program yang menghasilkan error tersebut:
trait Summary { fn describe(&self) -> String;}
trait Label { fn describe(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({} halaman)", self.title, self.pages) }}
impl Label for Book { fn describe(&self) -> String { format!("BUKU: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust for Crabs"), pages: 320 }; println!("{}", book.describe()); // ❌ milik Summary atau milik Label?}Apa artinya
Section titled “Apa artinya”Baca pesannya dari atas ke bawah:
^^^^^^^^di bawahdescribe: “multipledescribefound” (ditemukan lebih dari satudescribe). Rust mencari method bernamadescribepadaBookdan menemukan lebih dari satu.- Dua baris
note:menyebutkan kandidatnya: satu berasal dariimpl Label for Book(baris 21), satu lagi dariimpl Summary for Book(baris 15). Keduanya nyata, keduanya bisa dipakai, dan teks yang dikembalikan berbeda. - Dua baris
help:menunjukkan perbaikan untuk masing-masing pilihan.Summary::describe(&book)artinya “panggildescribeyang berasal dariSummary, dengan&booksebagaiself”.
Bayangkan ada dua rekan kerja yang sama-sama bernama Sam di satu kantor. Berteriak “Sam!” tidak akan berhasil; kamu bilang “Sam dari bagian keuangan”. Summary::describe adalah “Sam dari bagian keuangan”.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Pemanggilan method seperti book.describe() adalah jalan pintas. Rust melihat tipe book, lalu mencari di method milik tipe itu sendiri dan di method setiap trait yang ada di cakupan dan diimplementasikan untuk Book. Biasanya pencarian itu menemukan tepat satu yang cocok. Di sini ada dua, dan keduanya melakukan hal yang berbeda: satu mencetak ringkasan, satu lagi mencetak label rak.
Rust bisa saja memilih salah satu dengan aturan tersembunyi, misalnya “trait yang didefinisikan duluan”. Tapi kalau begitu, menambahkan sebuah trait, atau menukar urutan dua baris use, akan diam-diam mengubah apa yang dicetak programmu. Rust menolak menebak dan memintamu menyebutkannya sekali, di kode, di tempat yang bisa dilihat setiap pembaca.
Ada satu pengecualian: method di blok impl Book milik tipe itu sendiri selalu menang atas method trait dengan nama yang sama, jadi kasus itu tidak pernah memberi E0034. Kamu bisa membaca lebih lanjut di Trait dan di Trait tingkat lanjut.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: Panggil lewat trait-nya: Trait::method(&x)
Section titled “Perbaikan 1: Panggil lewat trait-nya: Trait::method(&x)”Pilih ini kalau kamu butuh kedua method itu dan hanya ingin menyebutkan yang mana setiap kali memanggil. Ini persis yang disarankan rustc. Nilainya masuk sebagai argumen pertama, dengan & yang sama seperti yang diharapkan &self milik method itu:
trait Summary { fn describe(&self) -> String;}
trait Label { fn describe(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({} halaman)", self.title, self.pages) }}
impl Label for Book { fn describe(&self) -> String { format!("BUKU: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust for Crabs"), pages: 320 }; println!("{}", Summary::describe(&book)); println!("{}", Label::describe(&book));}Output:
Rust for Crabs (320 halaman)BUKU: RUST FOR CRABSKalau method-nya menerima &mut self, kirim &mut book. Kalau menerima self, kirim book (dan nilainya berpindah, sama seperti dengan titik).
Perbaikan 2: Fully qualified syntax: <Type as Trait>::method()
Section titled “Perbaikan 2: Fully qualified syntax: <Type as Trait>::method()”Pilih ini kalau fungsi yang bentrok tidak punya self, seperti constructor atau fn category(). Di situ tidak ada nilai yang bisa dilihat Rust, jadi Trait::category() tidak cukup: kamu juga harus menyebut tipenya. <Book as Summary>::category() dibaca “category yang dimiliki Book karena mengimplementasikan Summary”. Cara ini juga bisa dipakai untuk method:
trait Summary { fn category() -> String; fn describe(&self) -> String;}
trait Label { fn category() -> String;}
struct Book { title: String,}
impl Summary for Book { fn category() -> String { String::from("Nonfiksi") } fn describe(&self) -> String { format!("Sebuah buku berjudul {}", self.title) }}
impl Label for Book { fn category() -> String { String::from("Rak B") }}
fn main() { let book = Book { title: String::from("Rust for Crabs") }; println!("{}", <Book as Summary>::category()); println!("{}", <Book as Label>::category()); println!("{}", <Book as Summary>::describe(&book));}Output:
NonfiksiRak BSebuah buku berjudul Rust for CrabsMenulis Book::category() di sini juga memberi E0034, dengan petunjuk use fully-qualified syntax to disambiguate (pakai fully qualified syntax untuk memperjelas). Menulis Summary::category() saja memberi error lain, E0790, karena trait-nya tetap tidak tahu tipe mana yang kamu maksud.
Perbaikan 3: Ganti nama salah satu method
Section titled “Perbaikan 3: Ganti nama salah satu method”Pilih ini kalau kedua trait itu buatanmu sendiri dan nama yang sama itu kebetulan saja. Dua method yang melakukan hal berbeda layak punya nama berbeda, dan setelah itu book.describe() yang singkat bisa dipakai lagi. Sering kali ini perbaikan yang paling rapi:
trait Summary { fn describe(&self) -> String;}
trait Label { fn label_text(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({} halaman)", self.title, self.pages) }}
impl Label for Book { fn label_text(&self) -> String { format!("BUKU: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust for Crabs"), pages: 320 }; println!("{}", book.describe()); println!("{}", book.label_text());}Output:
Rust for Crabs (320 halaman)BUKU: RUST FOR CRABSTentu saja kamu tidak bisa mengganti nama method dari trait milik crate lain. Untuk yang seperti itu, pakai Perbaikan 1 atau Perbaikan 4.
Perbaikan 4: Import hanya trait yang kamu butuhkan
Section titled “Perbaikan 4: Import hanya trait yang kamu butuhkan”Pilih ini kalau trait-trait itu berada di modul atau crate yang berbeda. Method sebuah trait hanya bisa dipanggil dengan titik selama trait itu ada di cakupan. Jadi kalau satu bagian kodemu hanya butuh salah satunya, import yang itu saja di sana. Kamu bahkan bisa menaruh use di dalam fungsi, supaya hanya berlaku di fungsi itu:
mod report { pub trait Summary { fn describe(&self) -> String; }}
mod shop { pub trait Label { fn describe(&self) -> String; }}
struct Book { title: String,}
impl report::Summary for Book { fn describe(&self) -> String { format!("Sebuah buku berjudul {}", self.title) }}
impl shop::Label for Book { fn describe(&self) -> String { format!("BUKU: {}", self.title.to_uppercase()) }}
fn print_label(book: &Book) { use shop::Label; // hanya Label yang ada di cakupan di sini println!("{}", book.describe());}
fn main() { use report::Summary; // hanya Summary yang ada di cakupan di sini let book = Book { title: String::from("Rust for Crabs") }; println!("{}", book.describe()); print_label(&book);}Output:
Sebuah buku berjudul Rust for CrabsBUKU: RUST FOR CRABSPerhatikan bahwa blok impl menulis report::Summary dan shop::Label secara lengkap. Mengimplementasikan trait tidak butuh use; hanya pemanggilan dengan titik yang butuh. Lihat Modul untuk penjelasan lebih lanjut tentang use.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”to_string() buatanmu sendiri. Setiap tipe yang mengimplementasikan Display sudah otomatis mendapat to_string() dari trait standar ToString. Tambahkan trait buatanmu dengan method to_string, dan point.to_string() memberi E0034, dengan candidate #2 is defined in an impl of the trait `ToString` for the type `T` . Perbaikannya: ganti nama method-mu (pretty(), to_label()), atau panggil Pretty::to_string(&point).
Dua import prelude::*. Library sering menyediakan use some_crate::prelude::*; untuk memasukkan trait-trait mereka. Import dua prelude yang sama-sama menambahkan method draw() atau render() ke tipe yang sama, dan pemanggilan dengan titik menjadi ambigu. Perbaikannya: ganti salah satu glob dengan item yang benar-benar kamu pakai saja, atau panggil Render::draw(&data).
Fungsi generic dengan dua bound. Di fn show<T: Summary + Label>(item: &T), item.describe() memberi E0034. Hati-hati dengan saran rustc di sini: rustc menawarkan Label::describe(&item), padahal item sudah berupa referensi, jadi hasilnya E0277 (the trait bound `&T: Label` is not satisfied). Tulis Label::describe(item) tanpa & tambahan. Lihat Generics.
Error terkait
Section titled “Error terkait”- E0790: kamu memanggil fungsi trait tanpa
self, sepertiDefault::default()atauSummary::category(), tanpa menyebut tipenya. - E0599: masalah kebalikannya, tidak ada method dengan nama itu yang ditemukan. Sering kali trait-nya belum di-import.
- E0277: trait bound tidak terpenuhi, misalnya setelah
&tambahan diTrait::method(&x). - E0428: nama yang sama didefinisikan lebih dari sekali. Dua item dengan nama yang sama di satu modul, bukan dua method trait.
- Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Book mengimplementasikan Summary dan Label, dan keduanya punya fn describe(&self). Baris mana yang memanggil versi Label?
2. Kedua trait punya fn category() -> String tanpa self. Pemanggilan mana yang berhasil?
3. Book punya impl Book { fn describe(&self) ... } sendiri dan juga mengimplementasikan Summary, yang juga punya describe. Apa yang dilakukan book.describe()?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0034 — Rust error codes index (bahasa Inggris): penjelasan resmi, dengan kedua bentuk fully qualified.
- Advanced Traits — The Rust Book (bahasa Inggris): bagian tentang fully qualified syntax, dengan contoh
Pilot,Wizard, danHuman. - Disambiguating overlapping traits — Rust by Example (bahasa Inggris): contoh singkat yang bisa dijalankan tentang dua trait dengan method bernama sama.
- Traits — The Rust Book (bahasa Inggris): mendefinisikan dan mengimplementasikan trait, dan kenapa trait harus ada di cakupan supaya method-nya bisa dipakai.
- Method call expressions — The Rust Reference (bahasa Inggris): aturan persis yang diikuti Rust untuk menemukan method di balik
x.method().