Error Rust E0369: binary operation cannot be applied to type
E0369 artinya kamu memakai operator seperti ==, <, atau + pada tipe yang belum tahu cara melakukannya. Di Rust, operator tidak otomatis tersedia untuk struct dan enum buatanmu. Setiap operator berasal dari sebuah trait, dan tipemu baru mendapatkannya kalau kamu men-derive atau mengimplementasikan trait itu. Perbaikannya biasanya cukup satu baris #[derive(...)], dan compiler bahkan memberitahumu yang mana. Untuk error lain di seri ini, lihat Penjelasan error compiler Rust.
Pesan error-nya
Section titled “Pesan error-nya”error[E0369]: binary operation `==` cannot be applied to type `Point` --> src/main.rs:9:10 |9 | if a == b { // ❌ Point tidak tahu cara membandingkan dirinya | - ^^ - Point | | | Point |note: an implementation of `PartialEq` might be missing for `Point` --> src/main.rs:1:1 |1 | struct Point { | ^^^^^^^^^^^^ must implement `PartialEq`help: consider annotating `Point` with `#[derive(PartialEq)]` |1 + #[derive(PartialEq)]2 | struct Point { |Compiler bilang operator == tidak bisa dipakai untuk Point, karena Point belum mengimplementasikan PartialEq, lalu menyarankan menambahkan #[derive(PartialEq)].
Ini program yang menghasilkan error tersebut:
struct Point { x: i32, y: i32,}
fn main() { let a = Point { x: 1, y: 2 }; let b = Point { x: 1, y: 2 }; if a == b { // ❌ Point tidak tahu cara membandingkan dirinya println!("Same point!"); }}Baris pertama pesannya berubah sesuai operatornya, tapi error-nya tetap sama:
binary operation `<` cannot be applied to type `Version`:PartialOrdtidak ada (juga untuk>,<=,>=).cannot add `Point` to `Point`:Addtidak ada. Kamu juga akan melihat “cannot subtract”, “cannot multiply”, dan seterusnya.cannot add `&str` to `&str`: kamu mencoba menggabungkan dua string slice dengan+.
Apa artinya
Section titled “Apa artinya”Label di bawah operator menunjukkan tipe di setiap sisi, di sini Point dan Point. Lalu note: menunjuk struct-mu dan bilang struct itu must implement PartialEq (wajib mengimplementasikan PartialEq). Dengan kata lain: “kamu memintaku membandingkan dua Point, tapi tidak ada yang memberitahuku apa arti sama untuk sebuah Point.”
Bayangkan seorang wasit yang diberi dua benda lalu ditanya “mana yang lebih besar?”. Untuk angka, jawabannya jelas. Untuk dua ransel, wasit itu butuh aturan dulu: berdasarkan berat? volume? Rust adalah wasit yang teliti itu.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Setiap operator sebenarnya adalah pemanggilan method yang menyamar. a == b memanggil PartialEq::eq, a < b memanggil PartialOrd::partial_cmp, dan a + b memanggil Add::add. Angka, String, dan banyak tipe standar lain sudah mengimplementasikan trait-trait ini. Struct barumu dimulai tanpa satu pun.
Rust tidak menebak, karena tebakan kadang salah. Apakah dua User sama kalau ID-nya sama, atau hanya kalau semua field-nya sama? Apakah Point + Point menjumlahkan koordinatnya, atau apakah menjumlahkan titik itu bahkan masuk akal? Kamu yang memutuskan, dengan men-derive trait-nya (default: field demi field) atau menulis impl sendiri. Trait-trait ini dijelaskan di halaman Trait yang sering dipakai.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: #[derive(PartialEq)] untuk == dan !=
Section titled “Perbaikan 1: #[derive(PartialEq)] untuk == dan !=”Pilih ini untuk == dan != kalau “sama” berarti “semua field-nya sama”. Sering kali inilah perbaikan yang tepat, dan persis inilah yang disarankan rustc. Tambahkan juga Debug supaya struct-nya bisa dicetak:
#[derive(Debug, PartialEq)]struct Point { x: i32, y: i32,}
fn main() { let a = Point { x: 1, y: 2 }; let b = Point { x: 1, y: 2 }; let c = Point { x: 5, y: 0 }; if a == b { println!("Titiknya sama!"); } println!("{:?} != {:?}: {}", a, c, a != c);}Output:
Titiknya sama!Point { x: 1, y: 2 } != Point { x: 5, y: 0 }: trueSetiap field juga harus bisa dibandingkan. Kalau tipe sebuah field tidak punya PartialEq, derive juga di tipe itu. Enum bekerja dengan cara yang sama: #[derive(PartialEq)] pada enum Status membuat status == Status::Banned bisa dikompilasi.
Perbaikan 2: #[derive(PartialOrd)] untuk < dan >
Section titled “Perbaikan 2: #[derive(PartialOrd)] untuk < dan >”Pilih ini kalau tipemu punya urutan yang wajar. PartialOrd butuh PartialEq, jadi rustc menyarankan #[derive(PartialEq, PartialOrd)]. Versi hasil derive membandingkan field dari atas ke bawah: pertama major, dan hanya kalau keduanya sama, baru minor. Jadi urutan field di struct-mu penting:
#[derive(Debug, PartialEq, PartialOrd)]struct Version { major: u32, minor: u32,}
fn main() { let installed = Version { major: 1, minor: 4 }; let required = Version { major: 1, minor: 7 }; if installed < required { println!("Silakan update dari {:?}", installed); }
let next = Version { major: 2, minor: 0 }; println!("2.0 lebih baru dari 1.7: {}", next > required);}Output:
Silakan update dari Version { major: 1, minor: 4 }2.0 lebih baru dari 1.7: trueKalau kamu ingin memanggil .sort() pada Vec berisi tipemu, kamu juga butuh Eq dan Ord. Lihat Mengurutkan dan mencari.
Perbaikan 3: impl std::ops::Add untuk +
Section titled “Perbaikan 3: impl std::ops::Add untuk +”Pilih ini kalau menjumlahkan dua nilai bertipe sama punya arti yang jelas, seperti menjumlahkan koordinat atau uang. Tidak ada derive untuk operator matematika, jadi kamu menulis impl pendek. Output adalah tipe yang dihasilkan +:
use std::ops::Add;
#[derive(Debug, Clone, Copy, PartialEq)]struct Point { x: i32, y: i32,}
impl Add for Point { type Output = Point;
fn add(self, other: Point) -> Point { Point { x: self.x + other.x, y: self.y + other.y } }}
fn main() { let start = Point { x: 1, y: 2 }; let step = Point { x: 3, y: 4 }; let end = start + step; println!("{:?}", end); println!("{}", end == Point { x: 4, y: 6 });}Output:
Point { x: 4, y: 6 }trueSub, Mul, Div, dan Neg bekerja dengan cara yang sama, semuanya ada di std::ops. Karena Copy di-derive di sini, start dan step masih bisa dipakai setelah +. Selengkapnya di Trait yang sering dipakai dan Advanced traits.
Perbaikan 4: Menggabungkan string dengan + atau format!
Section titled “Perbaikan 4: Menggabungkan string dengan + atau format!”Pilih ini kalau error-nya bilang cannot add `&str` to `&str`. + pada string butuh String yang dimiliki (owned) di sisi kiri dan &str di sisi kanan. Bagian help dari rustc menyebutnya dengan kata-katanya sendiri: “create an owned String from a string reference” (buat String owned dari referensi string), jadi first.to_owned() + second. Dengan dua String, pinjam yang kanan: s1 + &s2. Dan format! selalu bisa dipakai, tanpa memindahkan apa pun:
fn main() { let first = String::from("Halo, "); let second = String::from("Ferris");
let shout = format!("{first}{second}!"); // meminjam keduanya let joined = first + &second; // first dipindahkan ke joined let short = "Hai, ".to_owned() + "kepiting"; // &str di kiri: jadikan owned dulu
println!("{shout}"); println!("{joined}"); println!("{short}"); println!("{second} masih ada");}Output:
Halo, Ferris!Halo, FerrisHai, kepitingFerris masih ada+ mengambil ownership String di sebelah kiri lalu menambahkan teks ke ujungnya. Itulah sebabnya first tidak bisa dipakai lagi setelah baris itu. Kalau ini membingungkan, pakai saja format!. Lihat String.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”Membandingkan di kode generik. Fungsi klasik largest gagal dengan binary operation `>` cannot be applied to type `&T`, karena T bisa tipe apa saja, termasuk tipe yang tidak punya urutan:
fn largest<T>(list: &[T]) -> &T { let mut largest = &list[0]; for item in list { if item > largest { // ❌ T belum tentu bisa dibandingkan largest = item; } } largest}Perbaikannya: tambahkan trait bound, persis seperti saran rustc: fn largest<T: PartialOrd>(list: &[T]) -> &T. Lihat Generics.
Memeriksa nilai enum. if status == Status::Banned gagal sampai enum-nya punya #[derive(PartialEq)]. Kalau kamu tidak bisa mengubah enum-nya (karena berasal dari crate lain), pakai matches!(status, Status::Banned) atau match.
Menggabungkan dua String. first + second dengan dua String bukan E0369: itu E0308 (“expected &str, found String”) dengan help: consider borrowing here (coba pinjam di sini), yaitu first + &second. Satu keluarga, perbaikannya sama dengan Perbaikan 4.
Membandingkan dua tipe yang berbeda. count == limit dengan i32 dan f64 menghasilkan E0308, dan name == 'F' dengan String dan char menghasilkan E0277 (“can’t compare String with char”). Patokannya: kamu mendapat E0369 kalau tipe di kiri sama sekali tidak punya versi operator itu, dan E0308 atau E0277 kalau punya, tapi tidak untuk tipe di sisi kanan tersebut. Perbaikannya: konversi salah satu sisi (count as f64 == limit, name == "F").
Error terkait
Section titled “Error terkait”- E0277: the trait bound is not satisfied (trait tidak terpenuhi). Operatornya ada untuk tipe di kiri, tapi tidak dengan tipe di kanan ini (
i32 + f64,String == char). - E0308: mismatched types (tipe tidak cocok). Ini yang kamu dapat untuk
String + Stringatau saat membandingkan dua struct yang berbeda. - E0599: no method found (method tidak ditemukan). Memanggil
.sort()atau.contains()bisa gagal dengan cara serupa kalau tipemu tidak punyaOrdatauPartialEq. - E0368: binary assignment operation
+=cannot be applied. Versi+=dari error ini. ImplementasikanAddAssigndaristd::ops. - Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Kamu menulis if order_a == order_b untuk struct Order dan mendapat E0369. Biasanya apa perbaikannya?
2. Dengan #[derive(PartialOrd)] pada struct Version { major: u32, minor: u32 }, bagaimana dua versi dibandingkan?
3. first dan second sama-sama &str. Ekspresi mana yang menggabungkan keduanya tanpa error?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0369 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini.
- Derivable Traits — The Rust Book, Appendix C (bahasa Inggris): apa yang kamu dapat saat men-derive
PartialEqdanPartialOrd, termasuk urutan perbandingan field demi field. - Storing UTF-8 Encoded Text with Strings — The Rust Book (bahasa Inggris): kenapa
+pada string butuhStringdi kiri dan&strdi kanan, dan kapan memakaiformat!. - Operator Overloading — Rust by Example (bahasa Inggris): contoh singkat yang bisa dijalankan tentang mengimplementasikan
Adduntuk tipe buatanmu. std::ops::Add— dokumentasi standard library (bahasa Inggris): traitAdd, tipeOutput-nya, dan menjumlahkan nilai dengan tipe berbeda.