Error Rust E0790: cannot call associated function on trait (tipe tidak disebut)
E0790 artinya kamu meminta sesuatu dari sebuah trait yang sebenarnya hanya bisa diberikan oleh sebuah tipe. Default::default() saja itu seperti bilang “berikan aku nilai default-nya” tanpa menyebut default apa. Error ini cukup sering muncul, dan perbaikannya biasanya cukup menaruh nama tipenya di depan: Settings::default(), bukan Default::default(). Halaman ini menunjukkan perbaikannya dulu, lalu menjelaskan beda antara trait dan tipe, supaya error ini tidak lagi mengejutkanmu. Halaman ini bagian dari Penjelasan error compiler Rust.
Pesan error-nya
Section titled “Pesan error-nya”error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type --> src/main.rs:8:20 |8 | let settings = Default::default(); // ❌ default dari tipe apa? | ^^^^^^^^^^^^^^^^^^ cannot call associated function of trait |help: use a fully-qualified path to a specific available implementation |8 | let settings = </* self type */ as Default>::default(); // ❌ default dari tipe apa? | +++++++++++++++++++ +Compiler bilang associated function sebuah trait tidak bisa dipanggil tanpa menyebut tipe impl-nya, lalu menyarankan path lengkap dengan tempat kosong untuk tipenya.
Ini program yang menghasilkan error tersebut:
#[derive(Debug, Default)]struct Settings { volume: u8, dark_mode: bool,}
fn main() { let settings = Default::default(); // ❌ default dari tipe apa? println!("{:?}", settings);}Kamu mendapat error yang sama kalau memanggil fungsi dari trait buatanmu sendiri lewat nama trait-nya, seperti Shape::new(). Di sini rustc melihat dua tipe yang mengimplementasikan Shape, jadi keduanya ditampilkan sebagai pilihan:
error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type --> src/main.rs:33:17 | 2 | fn new() -> Self; | ----------------- `Shape::new` defined here...33 | let shape = Shape::new(); | ^^^^^^^^^^^^ cannot call associated function of trait |help: use a fully-qualified path to one of the available implementations |33 | let shape = <Circle as Shape>::new(); | ++++++++++ +33 | let shape = <Square as Shape>::new(); | ++++++++++ +Bagian help: menawarkan dua path lengkap: satu untuk Circle dan satu untuk Square.
Untuk konstanta trait seperti Plugin::ID, kalimat utamanya sedikit berubah: cannot refer to the associated constant on trait without specifying the corresponding `impl` type (tidak bisa merujuk konstanta trait tanpa menyebut tipe impl-nya). Masalahnya sama, perbaikannya juga sama.
Apa artinya
Section titled “Apa artinya”Lihat apa yang ditandai rustc:
^^^^di bawahDefault::default(): “cannot call associated function of trait” (tidak bisa memanggil associated function milik trait).defaultadalah fungsi milik traitDefault, dan kamu memanggilnya lewat nama trait-nya.- Bagian help menyarankan
</* self type */ as Default>::default(). Bagian/* self type */adalah tempat kosong yang harus kamu isi. rustc bertanya: “defaultmilik tipe yang mana yang kamu maksud?” - Di contoh
Shape, rustc mengisi tempat kosong itu untukmu dengan setiap tipe yang ia temukan (Circle,Square). Kalau hanya satu tipe yang mengimplementasikan trait-nya, ia hanya menyarankan tipe itu.
Ini ide kuncinya. Trait adalah janji: “setiap tipe yang mengimplementasikan Default bisa membuat nilai awal dari dirinya sendiri”. Tipe adalah yang menepati janji itu: Settings, String, u32, dan Vec<T> masing-masing menepatinya dengan caranya sendiri. Default::default() meminta nilai dari janjinya itu sendiri, padahal janji tidak bisa membuat apa-apa. Hanya tipe yang bisa.
Bayangkan tulisan “Bisa dibungkus” yang dipasang di jendela banyak restoran. Kamu tidak bisa memesan makan malam dari tulisan itu; kamu harus memilih restorannya. Settings::default() berarti memilih restorannya.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Kebanyakan method trait menerima self, seperti circle.area(). Method seperti itu tidak pernah menyebabkan E0790, karena nilainya sudah memberi tahu Rust tipenya: circle adalah Circle, jadi yang dipanggil adalah area milik Circle. Selengkapnya ada di Trait.
Sebagian fungsi trait tidak punya self: constructor seperti new() dan default(), konversi seperti From::from, dan konstanta trait seperti ID. Tanpa nilai yang bisa dilihat, Rust hanya punya dua tempat untuk mengetahui tipenya: nama yang kamu tulis di depan (Settings::), atau tipe yang harus dimiliki hasilnya (let s: Settings = ...). Kalau dua-duanya tidak ada, Rust tidak bisa menebak. Banyak tipe mengimplementasikan Default, dan memilih salah satunya secara acak akan diam-diam mengubah perilaku programmu.
Ini sama seperti interface di Java atau C#. Kamu tidak bisa memanggil new pada sebuah interface; kamu butuh class yang mengimplementasikannya. Rust hanya memberitahumu soal ini saat compile, lengkap dengan tempat persis yang perlu diperbaiki.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: Panggil lewat tipenya
Section titled “Perbaikan 1: Panggil lewat tipenya”Pilih ini hampir selalu. Ganti nama trait-nya dengan nama tipenya. Ini perbaikan yang paling singkat, dan mudah dibaca: “Settings default”, “Circle baru”:
#[derive(Debug, Default)]struct Settings { volume: u8, dark_mode: bool,}
trait Shape { fn new() -> Self; fn name() -> String;}
struct Circle { radius: f64,}
impl Shape for Circle { fn new() -> Self { Circle { radius: 1.0 } } fn name() -> String { String::from("lingkaran") }}
fn main() { let settings = Settings::default(); // bukan Default::default() let shape = Circle::new(); // bukan Shape::new()
println!("{:?}", settings); println!("Sebuah {} dengan jari-jari {}", Circle::name(), shape.radius);}Output:
Settings { volume: 0, dark_mode: false }Sebuah lingkaran dengan jari-jari 1Circle::new() bisa dipakai walaupun new didefinisikan di trait, bukan di blok impl Circle. Rust mencari new pada Circle, menemukannya lewat impl Shape for Circle, lalu memanggilnya. Trait-nya harus ada di scope (didefinisikan di file yang sama, atau dibawa masuk dengan use).
Perbaikan 2: Beri anotasi pada variabelnya
Section titled “Perbaikan 2: Beri anotasi pada variabelnya”Pilih ini kalau kamu suka gaya Default::default(), atau nama tipenya panjang. Tulis tipenya pada variabel, dan dari situ Rust tahu default mana yang harus dipanggil:
#[derive(Debug, Default)]struct Settings { volume: u8, dark_mode: bool,}
fn main() { let settings: Settings = Default::default(); let small: u8 = 42; let big: u32 = From::from(small);
println!("{:?}", settings); println!("{}", big);}Output:
Settings { volume: 0, dark_mode: false }42Cara ini hanya bisa dipakai kalau fungsinya mengembalikan tipenya sendiri (Self), seperti default(), new(), dan from(). Cara ini tidak bisa untuk fn name() -> String: tahu bahwa hasilnya String tidak memberi tahu Rust shape mana yang kamu maksud. Untuk kasus itu, pakai Perbaikan 1 atau Perbaikan 3. Selengkapnya tentang anotasi di Tipe data.
Perbaikan 3: Fully qualified syntax <Type as Trait>::function()
Section titled “Perbaikan 3: Fully qualified syntax <Type as Trait>::function()”Inilah bentuk “sebutkan semuanya” yang disarankan rustc. Baca <Circle as Shape>::new() sebagai “new yang dimiliki Circle karena ia mengimplementasikan Shape”. Bentuk ini selalu berhasil, tapi Circle::new() lebih mudah dibaca, jadi kamu hanya perlu memakainya kalau bentuk pendeknya ambigu:
- tipenya mengimplementasikan dua trait yang punya fungsi bernama sama, atau
- tipenya punya fungsinya sendiri dengan nama itu, dan kamu ingin versi milik trait.
Di sini sebuah Sale bisa ditulis sebagai baris CSV atau sebagai laporan cetak, dan kedua trait punya header(). Menulis Sale::header() memberi E0034 (bahasa Inggris) “multiple applicable items in scope” (ada beberapa item yang cocok di scope), jadi kamu harus menyebut yang mana:
trait Csv { fn header() -> String;}
trait Report { fn header() -> String;}
struct Sale;
impl Csv for Sale { fn header() -> String { String::from("tanggal,jumlah") }}
impl Report for Sale { fn header() -> String { String::from("Penjualan bulanan") }}
fn main() { println!("{}", <Sale as Csv>::header()); println!("{}", <Sale as Report>::header());}Output:
tanggal,jumlahPenjualan bulananPelajaran Trait lanjutan punya contoh lain, termasuk method yang menerima &self.
Perbaikan 4: Di kode generic, pakai T:: dan turbofish
Section titled “Perbaikan 4: Di kode generic, pakai T:: dan turbofish”Pilih ini kalau kamu sedang menulis fungsi generic. Di dalam fn register<T: Plugin>(), tipenya adalah T, jadi panggil T::name() dan T::ID, bukan Plugin::name(). Saat memanggil fungsinya, berikan tipenya dengan turbofish ::<Type>, karena tidak ada argumen yang bisa dipakai Rust untuk mengetahuinya:
trait Plugin { const ID: u32; fn name() -> String;}
struct Logger;struct Backup;
impl Plugin for Logger { const ID: u32 = 7; fn name() -> String { String::from("logger") }}
impl Plugin for Backup { const ID: u32 = 12; fn name() -> String { String::from("backup") }}
fn register<T: Plugin>() -> String { format!("Plugin #{}: {}", T::ID, T::name())}
fn main() { println!("{}", register::<Logger>()); println!("{}", register::<Backup>());}Output:
Plugin #7: loggerPlugin #12: backupKalau kamu tidak menulis turbofish-nya dan hanya menulis register(), kamu mendapat E0283: “cannot infer type of the type parameter T” (tipe untuk parameter T tidak bisa disimpulkan). T juga sebuah tipe di sini, jadi T:: bekerja sama seperti Circle:: di Perbaikan 1. Lihat Generics untuk penjelasan lebih lanjut tentang parameter tipe.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”Default::default() tanpa petunjuk apa pun. let config = Default::default(); gagal, tapi pemanggilan yang sama berhasil di mana pun tipenya sudah diketahui: di field struct, di argumen fungsi, atau di sintaks “isi sisanya”:
let settings = Settings { volume: 8, ..Default::default() }; // OK: Rust tahu ini SettingsPerbaiki baris yang gagal dengan Settings::default() atau let config: Settings = Default::default();. Lihat Trait yang sering dipakai.
Di dalam fungsi generic. Menulis nama trait-nya, bukan T:
error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type --> src/main.rs:9:29 |2 | fn name() -> String; | -------------------- `Shape::name` defined here...9 | format!("This is a {}", Shape::name()) | ^^^^^^^^^^^^^ cannot call associated function of trait |help: use the fully-qualified path to the only available implementation |9 | format!("This is a {}", <Circle as Shape>::name()) | ++++++++++ +rustc menyarankan path lengkap ke satu-satunya implementasi yang tersedia.
Hati-hati: rustc menyarankan <Circle as Shape> karena sejauh ini Circle adalah satu-satunya shape. Kode itu memang bisa di-compile, tapi fungsinya akan selalu memakai nama milik Circle, apa pun T-nya. Perbaikan yang benar adalah T::name(), seperti di Perbaikan 4.
Konstanta trait. println!("{}", Plugin::ID); meminta sebuah angka dari janjinya, padahal hanya setiap plugin yang punya angka itu. Perbaikannya: Logger::ID, atau T::ID di kode generic.
From::from(x) tanpa tujuan. let total = From::from(42u8); memberi E0790, karena banyak tipe bisa dibuat dari u8. Perbaikannya: u32::from(42u8), atau let total: u32 = 42u8.into();. Lihat Konversi.
Error terkait
Section titled “Error terkait”- E0282: type annotations needed (anotasi tipe dibutuhkan). Kalau kamu memakai nilainya nanti, seperti
let c = Shape::new();laluc.area(), rustc melaporkan E0282 sebagai gantinya. Perbaikannya sama. - E0283: anotasi tipe dibutuhkan saat banyak tipe cocok, misalnya
Into::into(n)tanpa tujuan, atau fungsi generic yang dipanggil tanpa turbofish. - E0599: fungsinya tidak ditemukan.
Settings::default()memberi error ini kalauSettingstidak mengimplementasikanDefault; tambahkan#[derive(Default)]. Denganlet s: Settings = Default::default();, kesalahan yang sama muncul sebagai E0277. - E0424:
selfdipakai di fungsi yang tidak punya parameterself. Ini juga tentang fungsi yang dimiliki sebuah tipe, bukan oleh satu nilai. - Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. let opts = Default::default(); memberi E0790. Options sudah derive Default. Baris mana yang memperbaikinya?
2. Kenapa let s: Settings = Default::default(); bisa di-compile, tapi let n: String = Shape::name(); tetap memberi E0790?
3. Di dalam fn show<T: Shape>(), bagaimana seharusnya kamu memanggil fungsi trait name()?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0790 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini, dengan contoh singkat dan perbaikannya.
- Traits — The Rust Book (bahasa Inggris): apa itu trait, cara mengimplementasikannya, dan implementasi default.
- Advanced Traits — The Rust Book (bahasa Inggris): termasuk bagian tentang fully qualified syntax, dengan contoh klasik
<Dog as Animal>::baby_name(). - Disambiguating overlapping traits — Rust by Example (bahasa Inggris): contoh singkat yang bisa dijalankan untuk memilih di antara dua trait yang sama-sama punya method bernama sama.
Default— dokumentasi standard library (bahasa Inggris): deriveDefault, mengimplementasikannya sendiri, dan pola..Default::default().