Error Rust E0404: expected trait, found struct or derive macro (bukan trait)
E0404 artinya: Rust menemukan nama yang kamu tulis, tapi nama itu bukan trait, padahal hanya trait yang boleh ada di tempat itu. Tempat-tempat itu adalah impl ___ for Type, bound seperti T: ___, dan dyn ___ atau impl ___. Kasus yang paling sering justru mengejutkan banyak orang: impl Debug for Point tanpa import. Debug memang ada di scope, tapi sebagai makro #[derive(Debug)], bukan sebagai trait. Kasus umum lainnya adalah struct yang dipakai seolah-olah trait, yang sering terjadi kalau kamu terbiasa dengan class dan inheritance (pewarisan). Keduanya cepat diperbaiki. (Halaman ini bagian dari Penjelasan error compiler Rust.)
Pesan error-nya
Section titled “Pesan error-nya”error[E0404]: expected trait, found derive macro `Debug` --> src/main.rs:6:6 |6 | impl Debug for Point { // ❌ `Debug` di sini adalah derive macro, bukan trait | ^^^^^ not a trait |help: consider importing this trait instead |1 + use std::fmt::Debug; |Ini program yang menghasilkan error tersebut:
struct Point { x: i32, y: i32,}
impl Debug for Point { // ❌ `Debug` di sini adalah derive macro, bukan trait fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "Point di ({}, {})", self.x, self.y) }}
fn main() { let p = Point { x: 3, y: 4 }; println!("{:?}", p);}Apa artinya
Section titled “Apa artinya”^^^^^ menunjuk ke Debug di baris 6, dengan label “not a trait” (bukan trait). Judulnya menyebut apa yang ditemukan Rust sebagai gantinya: sebuah derive macro bernama Debug.
Itu karena ada dua hal yang bernama Debug. Makro Debug, yang kamu pakai sebagai #[derive(Debug)], ada di prelude, jadi setiap file bisa melihatnya. Trait std::fmt::Debug, yang kamu butuhkan untuk impl Debug for ..., tidak ada di prelude. Jadi Rust menemukan satu-satunya Debug yang bisa dilihatnya, menyadari bahwa itu makro, lalu berhenti. Baris help memberi import yang memasukkan trait-nya. Hash bekerja dengan cara yang sama: expected trait, found derive macro `Hash` , diperbaiki dengan use std::hash::Hash;.
Kalau judulnya bilang found struct `Animal` (atau enum, atau type alias), nama itu adalah salah satu tipe buatanmu sendiri, yang dipakai di tempat yang seharusnya diisi trait.
Ini seperti meminta “seorang sopir bersertifikat” lalu malah diberi sebuah mobil. Mobil itu nyata, dan sangat berkaitan, tapi pekerjaannya membutuhkan seseorang yang bisa menyetir: sekumpulan kemampuan, dan itulah trait.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Rust memisahkan data dari perilaku. Struct atau enum menggambarkan data: sebuah nilai itu apa. Trait menggambarkan perilaku: sebuah tipe bisa melakukan apa. impl Trait for Type menghubungkan keduanya: “tipe ini bisa melakukan hal-hal ini”. Bound seperti T: Trait berarti “tipe apa pun yang bisa melakukan hal-hal ini”.
Struct tidak bisa dipakai di tempat-tempat itu, karena struct bukan daftar kemampuan. Dan Rust tidak punya inheritance, jadi impl Animal for Dog tidak bisa berarti “Dog adalah sejenis Animal” kalau Animal adalah struct. Kalau kamu ingin beberapa tipe berbagi perilaku, bagian yang dibagi itu adalah trait. Lihat Trait dan Generics.
Rust juga menyimpan makro dan trait di namespace yang terpisah, itulah sebabnya Debug bisa menjadi makro sekaligus trait. Derive macro-nya ada di prelude supaya #[derive(Debug)] bisa dipakai di mana saja; trait-nya hanya perlu disebut saat kamu sendiri mengimplementasikannya atau memakainya sebagai bound.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: Import trait-nya (use std::fmt::Debug;)
Section titled “Perbaikan 1: Import trait-nya (use std::fmt::Debug;)”Pilih ini kalau pesannya bilang found derive macro dan kamu ingin format sendiri, atau kamu butuh trait itu di bound seperti T: Debug. Tambahkan baris dari help:
use std::fmt::Debug;
struct Point { x: i32, y: i32,}
impl Debug for Point { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "Point di ({}, {})", self.x, self.y) }}
fn print_all<T: Debug>(items: &[T]) { for item in items { println!("{:?}", item); }}
fn main() { let p = Point { x: 3, y: 4 }; println!("{:?}", p); print_all(&[Point { x: 0, y: 0 }, Point { x: -1, y: 7 }]);}Output:
Point di (3, 4)Point di (0, 0)Point di (-1, 7)Alternatif yang sering dipakai adalah use std::fmt;, lalu impl fmt::Debug for Point dengan fmt::Formatter dan fmt::Result. Keduanya sama-sama boleh; pilih satu gaya per file.
Perbaikan 2: Pakai #[derive(Debug)] alih-alih menulis impl-nya
Section titled “Perbaikan 2: Pakai #[derive(Debug)] alih-alih menulis impl-nya”Pilih ini kalau kamu tidak butuh format kustom, dan untuk Debug itu yang paling sering terjadi. Derive macro menuliskan seluruh impl Debug for Point untukmu, dan kamu tidak butuh import apa pun:
#[derive(Debug)] // ✅ derive macro menuliskan `impl Debug for Point` untukmustruct Point { x: i32, y: i32,}
fn main() { let p = Point { x: 3, y: 4 }; println!("{:?}", p); println!("{:#?}", p); println!("jarak dari (0, 0) di grid: {}", p.x.abs() + p.y.abs());}Output:
Point { x: 3, y: 4 }Point { x: 3, y: 4,}jarak dari (0, 0) di grid: 7Pakai salah satunya, jangan keduanya: #[derive(Debug)] ditambah impl Debug buatanmu sendiri memberi error “conflicting implementations” (implementasi yang bentrok, E0119, bahasa Inggris). {:#?} adalah bentuk yang rapi dan multi-baris. Selengkapnya di Trait yang Sering Dipakai.
Perbaikan 3: Definisikan trait untuk perilaku yang dibagi
Section titled “Perbaikan 3: Definisikan trait untuk perilaku yang dibagi”Pilih ini kalau pesannya bilang found struct di baris impl X for Y. Kamu mungkin ingin Dog menjadi “sejenis Animal”, seperti subclass:
error[E0404]: expected trait, found struct `Animal` --> src/main.rs:7:6 |7 | impl Animal for Dog { // ❌ `Animal` itu struct, bukan trait | ^^^^^^ not a traitProgram di baliknya:
struct Animal { name: String,}
struct Dog;
impl Animal for Dog { // ❌ `Animal` itu struct, bukan trait fn name(&self) -> String { String::from("Rex") }}
fn main() { println!("{}", Dog.name());}Di Rust, bagian yang dibagi adalah sebuah trait: daftar method yang harus disediakan setiap tipe, ditambah method default kalau perlu. Datanya tetap ada di masing-masing struct:
trait Animal { // ✅ sebuah trait: apa yang bisa dilakukan setiap hewan fn name(&self) -> String;
fn greet(&self) -> String { // method default format!("Halo, aku {}!", self.name()) }}
struct Dog;
struct Cat { name: String, // datanya tinggal di struct}
impl Animal for Dog { fn name(&self) -> String { String::from("Rex") }}
impl Animal for Cat { fn name(&self) -> String { self.name.clone() }}
fn main() { println!("{}", Dog.greet()); let tom = Cat { name: String::from("Tom") }; println!("{}", tom.greet());}Output:
Halo, aku Rex!Halo, aku Tom!Kalau kamu hanya ingin menambah method ke satu struct, kamu sama sekali tidak butuh trait: tulis impl Dog { fn name(&self) -> String { ... } }, tanpa for. Lihat Mendefinisikan trait.
Perbaikan 4: Terima struct-nya langsung, atau pakai bound pada trait
Section titled “Perbaikan 4: Terima struct-nya langsung, atau pakai bound pada trait”Pilih ini kalau error-nya menunjuk ke bound seperti T: Shape atau impl Shape, dan Shape adalah struct buatanmu:
error[E0404]: expected trait, found struct `Shape` --> src/main.rs:5:16 |5 | fn describe<T: Shape>(item: &T) { // ❌ `Shape` itu struct, bukan trait | ^^^^^ not a traitProgram di baliknya:
struct Shape { sides: u32,}
fn describe<T: Shape>(item: &T) { // ❌ `Shape` itu struct, bukan trait println!("a shape");}
fn main() { describe(&Shape { sides: 3 });}Bound berarti “tipe apa pun yang mengimplementasikan trait ini”. Kalau fungsinya hanya pernah menerima Shape, buang generic-nya dan terima &Shape. Kalau fungsinya harus bisa dipakai untuk banyak tipe, taruh perilaku yang dibagi di sebuah trait dan pakai trait itu sebagai bound:
struct Shape { sides: u32,}
fn describe(item: &Shape) { // ✅ terima struct-nya langsung, tidak perlu generic println!("bentuk dengan {} sisi", item.sides);}
trait Named { // ✅ atau sebuah trait, untuk "tipe apa pun yang punya nama" fn name(&self) -> String;}
impl Named for Shape { fn name(&self) -> String { match self.sides { 3 => String::from("segitiga"), 4 => String::from("persegi"), n => format!("segi-{}", n), } }}
fn shout<T: Named>(item: &T) { // ✅ trait bound butuh sebuah trait println!("{}!", item.name().to_uppercase());}
fn main() { let t = Shape { sides: 3 }; describe(&t); shout(&t); shout(&Shape { sides: 6 });}Output:
bentuk dengan 3 sisiSEGITIGA!SEGI-6!Trait bound dijelaskan di Generics dan Trait.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”impl Vec<i32> sebagai tipe parameter. fn total(x: impl Vec<i32>) memberi expected trait, found struct `Vec` . impl di posisi itu butuh sebuah trait. Perbaikannya: terima &[i32] atau Vec<i32>, atau impl IntoIterator<Item = i32> kalau kamu ingin menerima koleksi apa saja.
Box<dyn String>. dyn juga butuh trait, jadi ini memberi expected trait, found struct `String` , dan rustc menyarankan ToString. Perbaikannya: Box<dyn ToString> kalau yang kamu maksud “apa pun yang bisa menjadi string”, atau cukup String. Lihat Trait Object.
Type alias untuk sebuah trait. type Printable = dyn std::fmt::Display; lalu impl Printable for A memberi expected trait, found type alias `Printable` dengan label “type aliases cannot be used as traits” (type alias tidak bisa dipakai sebagai trait). Perbaikannya: tulis nama asli trait-nya di impl.
Error terkait
Section titled “Error terkait”- E0405: cannot find trait in this scope: namanya sama sekali tidak ada, seperti
Displaytanpa import. E0404 berarti namanya ada, tapi jenisnya salah. - E0277: the trait bound is not satisfied: trait-nya benar, tapi tipenya tidak mengimplementasikannya.
- E0425: cannot find value in this scope: error “tidak ditemukan” yang sejenis untuk nilai, fungsi, dan tipe.
- E0107: wrong number of generic arguments: trait atau tipenya benar, tapi argumen
<...>-nya terlalu banyak atau terlalu sedikit. - E0119: conflicting implementations (bahasa Inggris):
#[derive(Debug)]danimpl Debugbuatanmu sendiri pada tipe yang sama. - Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. impl Debug for Point memberi "expected trait, found derive macro Debug". Apa perbaikan paling sederhana kalau kamu ingin format kustom?
2. Kamu punya struct Animal dan menulis impl Animal for Dog. Apa yang diinginkan Rust sebagai gantinya?
3. Mana yang boleh ditulis setelah T: di fn show<T: ___>(x: T)?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0404 — Rust error codes index (bahasa Inggris): penjelasan resmi, dengan contoh struct yang dipakai sebagai trait.
- Defining Shared Behavior with Traits — The Rust Book (bahasa Inggris): mendefinisikan trait, mengimplementasikannya, dan memakainya sebagai bound.
std::fmt::Debug— standard library docs (bahasa Inggris): men-deriveDebugdibandingkan mengimplementasikannya sendiri.- Traits — Rust by Example (bahasa Inggris): sebuah trait yang dipakai bersama oleh beberapa tipe, dengan method default.
std::prelude— standard library docs (bahasa Inggris): apa saja yang ada di scope di setiap file, termasuk derive macroDebugtapi bukan trait-nya.