Error Rust E0277: the trait bound is not satisfied (trait tidak terpenuhi)
E0277 artinya: kamu memakai sebuah tipe di tempat yang membutuhkan kemampuan yang tidak dimiliki tipe itu. Mencetaknya, membandingkannya, menjumlahkannya, mengonversinya, mengirimnya ke thread lain: masing-masing butuh sebuah trait, dan tipemu belum punya trait itu. Ini salah satu error Rust yang paling sering muncul. Pesannya biasanya sudah menyebutkan perbaikan yang tepat, dan sering kali perbaikannya cuma satu baris. (Halaman ini bagian dari Penjelasan error compiler Rust.)
Pesan error-nya
Section titled “Pesan error-nya”Versi yang paling sering ditemui pertama kali adalah saat mencetak struct buatan sendiri dengan {}:
error[E0277]: `Player` doesn't implement `std::fmt::Display` --> src/main.rs:8:20 |8 | println!("{}", p); | -- ^ `Player` cannot be formatted with the default formatter | | | required by this formatting parameter |help: the trait `std::fmt::Display` is not implemented for `Player` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) insteadCompiler bilang Player tidak mengimplementasikan Display, jadi tidak bisa dicetak dengan {}, dan menyarankan {:?} sebagai gantinya.
Program yang menyebabkannya:
struct Player { name: String, score: u32,}
fn main() { let p = Player { name: String::from("Ana"), score: 42 }; println!("{}", p);}Apa artinya
Section titled “Apa artinya”Trait adalah janji bahwa sebuah tipe bisa melakukan sesuatu. Display adalah janji “aku tahu cara mencetak diriku dengan rapi”. PartialEq artinya “aku bisa dibandingkan dengan ==”. Send artinya “aku aman dipindahkan ke thread lain”.
Kode seperti println!, .sort(), +, dan thread::spawn meminta salah satu janji itu. Compiler menunjuk nilai yang kamu berikan (p di baris 8) dan bilang: tipe ini tidak pernah membuat janji itu, jadi aku tidak bisa memakainya di sini.
Bayangkan stopkontak. Stopkontak hanya menerima steker dengan satu bentuk tertentu. Alatmu mungkin berfungsi sempurna, tapi kalau stekernya tidak pas, ya tidak bisa dicolokkan. E0277 adalah cara compiler bilang “stekernya salah”. Kamu memperbaikinya dengan memberi tipe itu steker yang benar: biasanya lewat #[derive(...)], kadang dengan blok impl pendek.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Rust tidak pernah menebak. Untuk tipe Player buatanmu, tidak ada satu cara “yang benar” untuk mencetak, membandingkan, atau mengurutkannya. Apakah dua pemain sama kalau namanya sama, atau hanya kalau semua field-nya sama? Rust memintamu menyatakannya, alih-alih diam-diam melakukan sesuatu yang tidak kamu maksud.
Pemeriksaan ini terjadi saat kompilasi. Di banyak bahasa, mencetak objek yang belum diajari cara mencetak dirinya menghasilkan sesuatu yang tidak berguna seperti <Player object at 0x7f...>, dan mengirim nilai yang tidak aman untuk thread bisa berujung crash langka di production. Di Rust, keduanya adalah error kompilasi yang cukup kamu perbaiki sekali.
Cara kerja trait dijelaskan di halaman Trait, dan trait sehari-hari (Debug, Display, PartialEq, Ord, Hash) di Trait yang sering dipakai.
Cara memperbaikinya
Section titled “Cara memperbaikinya”E0277 punya banyak wajah. Cari pesan yang cocok dengan error-mu di bawah ini.
Perbaikan 1: Mencetak struct dengan {} atau {:?}
Section titled “Perbaikan 1: Mencetak struct dengan {} atau {:?}”{:?} butuh Debug. Kamu hampir tidak pernah menulisnya sendiri: tambahkan #[derive(Debug)]. Pakai ini untuk debugging dan log. Versi error untuk {:?} terlihat seperti ini:
error[E0277]: `Player` doesn't implement `Debug` = help: the trait `Debug` is not implemented for `Player` = note: add `#[derive(Debug)]` to `Player` or manually `impl Debug for Player`Catatannya bilang: tambahkan #[derive(Debug)] ke Player, atau tulis impl Debug for Player sendiri.
{} butuh Display. Rust tidak bisa men-derive-nya, karena hanya kamu yang tahu seperti apa versi “rapi”-nya. Tulis impl kecil dengan write!. Pakai ini untuk teks yang akan dilihat pengguna.
use std::fmt;
#[derive(Debug)] // {:?} langsung tersediastruct Player { name: String, score: u32,}
// Display kamu tulis sendiri: ini menentukan apa yang dicetak {}.impl fmt::Display for Player { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} ({} poin)", self.name, self.score) }}
fn main() { let p = Player { name: String::from("Ana"), score: 42 }; println!("{:?}", p); println!("{}", p);}Output:
Player { name: "Ana", score: 42 }Ana (42 poin)Perbaikan 2: Membandingkan, mengurutkan, atau memakai sebagai key HashMap
Section titled “Perbaikan 2: Membandingkan, mengurutkan, atau memakai sebagai key HashMap”.contains() dan == butuh PartialEq. .sort() butuh Ord. Key HashSet dan HashMap butuh Eq dan Hash. Pesannya terlihat seperti ini:
error[E0277]: can't compare `Point` with `Point` = help: the trait `PartialEq` is not implemented for `Point`
error[E0277]: the trait bound `Priority: Ord` is not satisfied = help: the trait `Ord` is not implemented for `Priority`
error[E0277]: the trait bound `Point: Hash` is not satisfied = help: the trait `Hash` is not implemented for `Point`Ketiganya bilang hal yang sama: trait tertentu (PartialEq, Ord, Hash) belum diimplementasikan untuk tipemu. Perbaikannya adalah men-derive trait-trait itu. Mereka saling bergantung, jadi set lengkap yang biasa dipakai adalah PartialEq, Eq, Hash, PartialOrd, Ord:
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]enum Priority { Low, Medium, High,}
fn main() { let mut tasks = vec![Priority::High, Priority::Low, Priority::Medium]; tasks.sort(); // butuh Ord println!("{:?}", tasks); println!("Ada yang High: {}", tasks.contains(&Priority::High)); // butuh PartialEq
let mut counts: HashMap<Priority, u32> = HashMap::new(); // key butuh Eq + Hash for task in &tasks { *counts.entry(*task).or_insert(0) += 1; } println!("Tugas Low: {}", counts[&Priority::Low]);}Output:
[Low, Medium, High]Ada yang High: trueTugas Low: 1Ord hasil derive mengurutkan varian enum sesuai urutan kamu menuliskannya, dan struct field demi field dari atas ke bawah. Satu catatan jujur: tipe dengan field f64 tidak bisa men-derive Eq, Hash, atau Ord, karena float punya NaN, yang tidak sama dengan dirinya sendiri. Untuk mengurutkan float, lihat Mengurutkan dan mencari.
Perbaikan 3: Mencampur tipe angka (cannot add f64 to i32)
Section titled “Perbaikan 3: Mencampur tipe angka (cannot add f64 to i32)”error[E0277]: cannot add `f64` to `i32` --> src/main.rs:4:23 |4 | let total = items + price; | ^ no implementation for `i32 + f64` | = help: the trait `Add<f64>` is not implemented for `i32`Artinya: tidak ada cara menjumlahkan i32 dengan f64. Rust tidak pernah mengonversi angka untukmu, bahkan i32 ke f64 pun tidak. Kedua sisi +, -, *, dan / harus bertipe sama. Konversi salah satu sisi sendiri dengan as:
fn main() { let items: i32 = 3; let price: f64 = 2.5; let total = items as f64 * price; // konversi dulu, baru dikalikan println!("Total: {total}");}Output:
Total: 7.5f64::from(items) juga bisa, dan tidak mungkin kehilangan data. Arah sebaliknya (f64 ke i32) membuang angka desimalnya, jadi pikirkan pembulatan dulu. Halaman Konversi tipe dan Konversi membahas detailnya.
Perbaikan 4: ? couldn’t convert the error
Section titled “Perbaikan 4: ? couldn’t convert the error”error[E0277]: `?` couldn't convert the error to `ConfigError` |8 | fn parse_port(text: &str) -> Result<u16, ConfigError> { | ------------------------ expected `ConfigError` because of this9 | let port = text.parse::<u16>()?; | --------------^ the trait `From<ParseIntError>` is not implemented for `ConfigError` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` traitPesan ini berarti ? tidak bisa mengubah ParseIntError menjadi ConfigError, karena tidak ada From<ParseIntError> untuk ConfigError. Saat ? bertemu error, ia mengembalikan error itu dari fungsimu. Kalau tipe error-nya berbeda dengan yang dijanjikan fungsimu, ? mencoba mengonversinya dengan From. Tugasmu adalah menulis From itu:
use std::num::ParseIntError;
#[derive(Debug)]enum ConfigError { BadPort(ParseIntError),}
// Ajari `?` cara mengubah ParseIntError menjadi ConfigError.impl From<ParseIntError> for ConfigError { fn from(err: ParseIntError) -> Self { ConfigError::BadPort(err) }}
fn parse_port(text: &str) -> Result<u16, ConfigError> { let port = text.parse::<u16>()?; Ok(port)}
fn main() { println!("{:?}", parse_port("8080")); println!("{:?}", parse_port("delapan puluh"));}Output:
Ok(8080)Err(BadPort(ParseIntError { kind: InvalidDigit }))Pilihan bagus lainnya: konversi satu pemanggilan saja dengan .map_err(ConfigError::BadPort)?, pakai #[from] dari crate thiserror, atau kembalikan Result<T, Box<dyn std::error::Error>> di program kecil. Lihat Error kustom dan anyhow dan thiserror.
Perbaikan 5: Rc cannot be sent between threads safely
Section titled “Perbaikan 5: Rc cannot be sent between threads safely”error[E0277]: `Rc<String>` cannot be sent between threads safely --> src/main.rs:6:32 |6 | let handle = thread::spawn(move || { | ------------- required by a bound introduced by this call ... = help: within `{closure@src/main.rs:6:32: 6:39}`, the trait `Send` is not implemented for `Rc<String>`Artinya Rc<String> tidak aman dikirim ke thread lain, karena ia tidak Send. Rc menghitung owner-nya dengan angka biasa, yang bisa diubah dua thread sekaligus sampai rusak. Karena itu Rc bukan Send. Pakai Arc (versi “atomic”) sebagai gantinya. Cara kerjanya sama, dan aman dipakai lintas thread:
use std::sync::Arc;use std::thread;
fn main() { let config = Arc::new(String::from("debug")); let for_thread = Arc::clone(&config); // owner kedua untuk thread-nya
let handle = thread::spawn(move || { println!("Thread melihat: {for_thread}"); }); handle.join().unwrap();
println!("Main masih punya: {config}");}Output:
Thread melihat: debugMain masih punya: debugHal yang sama berlaku untuk RefCell: lintas thread, pakai Arc<Mutex<T>>, bukan Rc<RefCell<T>>. Lihat Shared state dan Konkurensi.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”- Fungsi generik.
fn announce<T: Display>(item: T)yang dipanggil dengan struct-mu menghasilkan`Song` doesn't implement `std::fmt::Display`dengan keterangan “required by a bound inannounce” (dibutuhkan oleh batasan diannounce). Perbaikannya: implementasikan trait yang diminta batasan itu. - Test.
assert_eq!(a, b)butuhPartialEqdanDebug(untuk mencetak nilainya kalau test gagal). Perbaikannya:#[derive(Debug, PartialEq)]. .collect()ke tipe yang salah.let nums: Vec<i32> = words.iter().collect();menghasilkan “a value of typeVec<i32>cannot be built from an iterator over elements of type&String” (Vec<i32>tidak bisa dibangun dari iterator berisi&String). Perbaikannya: konversi setiap item dulu, misalnya.map(|w| w.parse().unwrap()). Lihat Iterator.
Error terkait
Section titled “Error terkait”- E0599: no method found (method tidak ditemukan). Memanggil
.insert()padaHashMapyang key-nya tidakHashkadang muncul sebagai E0599 (“trait bounds were not satisfied”). Perbaikannya sama: derive trait-nya. - E0308: mismatched types (tipe tidak cocok). Kamu memberikan satu tipe di tempat yang mengharapkan tipe lain, misalnya
i32di tempatf64. - E0382: borrow of moved value (nilai sudah dipindah). Sering muncul tepat setelah kamu menambahkan
CloneatauCopysambil memikirkan trait. - E0369: binary operation
==cannot be applied. Versi operator==dariPartialEqyang hilang. Perbaikannya sama. - Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Kamu mendapat "Order doesn't implement Debug" saat memakai {:?}. Apa perbaikan tercepatnya?
2. Kenapa let total = 3_i32 + 2.5_f64; gagal?
3. Kode thread-mu gagal dengan "Rc<String> cannot be sent between threads safely". Apa yang sebaiknya kamu pakai?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0277 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini.
- Traits: Defining Shared Behavior — The Rust Book (bahasa Inggris): apa itu trait dan bagaimana trait bound bekerja.
- Derivable Traits — The Rust Book, Appendix C (bahasa Inggris): apa yang kamu dapat saat men-derive
Debug,PartialEq,Eq,Hash,PartialOrd, danOrd. - Recoverable Errors with Result — The Rust Book (bahasa Inggris): cara kerja
?, termasuk konversiFrom-nya. Send— standard library Rust (bahasa Inggris): tipe mana yang bisa berpindah antar-thread, dan kenapaRctidak bisa.