Lewati ke konten

Error Rust E0368: binary assignment operation += cannot be applied to type

E0368 artinya kamu memakai operator gabungan seperti +=, -=, atau *= pada tipe yang belum mendukungnya. Error ini sepupu += dari E0369. Struct buatanmu tidak otomatis mendapat +=, bahkan kalau sudah mendukung + sekalipun. Perbaikannya biasanya blok impl AddAssign yang pendek, dan untuk angka di balik referensi cukup satu *. Untuk error lain di seri ini, lihat Penjelasan error compiler Rust.

error[E0368]: binary assignment operation `+=` cannot be applied to type `Money`
--> src/main.rs:8:5
|
8 | wallet += tip; // ❌ Money tidak tahu cara melakukan +=
| ------^^^^^^^
| |
| cannot use `+=` on type `Money`
|
note: an implementation of `AddAssign` might be missing for `Money`
--> src/main.rs:1:1
|
1 | struct Money {
| ^^^^^^^^^^^^ must implement `AddAssign`

Compiler bilang += tidak bisa dipakai pada Money, karena Money belum mengimplementasikan AddAssign.

Ini program yang menghasilkan error tersebut:

struct Money {
cents: i64,
}
fn main() {
let mut wallet = Money { cents: 500 };
let tip = Money { cents: 150 };
wallet += tip; // ❌ Money tidak tahu cara melakukan +=
println!("{}", wallet.cents);
}

Operator di baris pertama pesannya bisa berubah, tapi error-nya tetap sama. p -= step meminta SubAssign, dan p *= 2 meminta MulAssign<{integer}>, yang artinya “kalikan-lalu-isi dengan sebuah bilangan bulat”.

Label di bawah sisi kiri menjelaskan apa yang salah: “cannot use += on type Money” (tidak bisa memakai += pada tipe Money). Lalu note: menunjuk struct-mu dan bilang struct itu must implement AddAssign (wajib mengimplementasikan AddAssign). Itulah trait di balik +=.

Bayangkan sebuah celengan. Memasukkan koin ke dalamnya adalah tindakan yang jelas. Tapi kalau seseorang memberimu kotak berlabel “Money” dan bilang “tambahkan kotak lain ini ke situ”, kamu perlu tahu caranya: jumlahkan sennya? tumpuk kotaknya? Rust memintamu menuliskan aturan itu sekali saja.

Setiap operator gabungan sebenarnya adalah pemanggilan method yang menyamar. a += b memanggil AddAssign::add_assign(&mut a, b), a -= b memanggil SubAssign::sub_assign, dan seterusnya. Semua trait ini ada di std::ops. Angka sudah mengimplementasikannya. Struct barumu dimulai tanpa satu pun, dan tidak ada #[derive] untuk operator matematika.

Ini bagian yang sering mengejutkan: Add dan AddAssign adalah dua trait terpisah. Meskipun Point sudah mengimplementasikan Add, p += q tetap menghasilkan E0368. Alasannya, keduanya punya tugas berbeda. a + b menerima dua nilai dan mengembalikan nilai baru. a += b mengubah a di tempat lewat &mut self dan tidak mengembalikan apa-apa. Mengubah di tempat bisa jauh lebih hemat: += milik String menambahkan teks ke buffer yang sudah ada, bukan membangun string baru. Rust membiarkan setiap tipe memilih, jadi Rust tidak pernah diam-diam mengubah += menjadi + untukmu.

Trait operator dijelaskan di Trait yang sering dipakai dan Advanced traits.

Perbaikan 1: Implementasikan AddAssign (dan kawan-kawannya)

Section titled “Perbaikan 1: Implementasikan AddAssign (dan kawan-kawannya)”

Pilih ini kalau += pada tipemu punya arti yang jelas, seperti menambah uang atau menggeser posisi. Kamu menulis satu method yang mengubah self. Tambahkan SubAssign, MulAssign, dan yang lain dengan cara yang sama saat kamu membutuhkannya:

use std::ops::{AddAssign, SubAssign};
#[derive(Debug)]
struct Money {
cents: i64,
}
impl AddAssign for Money {
fn add_assign(&mut self, other: Money) {
self.cents += other.cents;
}
}
impl SubAssign for Money {
fn sub_assign(&mut self, other: Money) {
self.cents -= other.cents;
}
}
fn main() {
let mut wallet = Money { cents: 500 };
wallet += Money { cents: 150 }; // dapat tip
wallet -= Money { cents: 200 }; // beli makan siang
println!("{:?}", wallet);
}

Output:

Money { cents: 450 }
▶ Coba sendiri

Perhatikan &mut self dan tidak adanya tipe kembalian. Hanya itu bedanya dengan Add. Di dalamnya, self.cents += other.cents bisa jalan karena i64 sudah punya AddAssign.

Perbaikan 2: Sudah punya Add? Tulis a = a + b, atau pakai ulang

Section titled “Perbaikan 2: Sudah punya Add? Tulis a = a + b, atau pakai ulang”

Pilih ini kalau tipemu sudah mengimplementasikan Add (mungkin setelah memperbaiki E0369). Kalau tipenya Copy, kamu bisa langsung menulis pos = pos + step, yang hanya butuh Add. Atau implementasikan AddAssign dalam satu baris dengan memanggil Add milikmu, supaya += juga bisa dipakai:

use std::ops::{Add, AddAssign};
#[derive(Debug, Clone, Copy)]
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 }
}
}
impl AddAssign for Point {
fn add_assign(&mut self, other: Point) {
*self = *self + other; // pakai ulang Add
}
}
fn main() {
let mut pos = Point { x: 0, y: 0 };
let step = Point { x: 1, y: 2 };
pos = pos + step; // hanya butuh Add
pos += step; // butuh AddAssign
println!("{:?}", pos);
}

Output:

Point { x: 2, y: 4 }
▶ Coba sendiri

*self = *self + other menyalin nilai saat ini keluar, menjumlahkannya, lalu menulis hasilnya kembali. Ini hanya bisa karena Point adalah Copy. Untuk tipe yang memiliki data di heap (misalnya punya field Vec), tulis versi di tempat seperti di Perbaikan 1.

Perbaikan 3: String: s += &t atau push_str

Section titled “Perbaikan 3: String: s += &t atau push_str”

Pilih ini kalau sisi kirinya string. += bekerja pada String yang dimiliki (owned), dan sisi kanannya harus &str. Ada dua kasus yang sering menjebak:

  • cannot be applied to type `&str`: sisi kirinya string slice seperti "Hello, ". &str adalah tampilan yang hanya bisa dibaca, jadi tidak bisa bertambah panjang. Jadikan String dulu.
  • s += t dengan t yang juga String bukan E0368. Itu E0308, “expected &str, found String”, dengan saran s += &t. Cukup pinjam sisi kanannya.
fn main() {
let mut greeting = String::from("Halo, "); // owned, jadi bisa bertambah
let name = String::from("Ferris");
greeting += &name; // pinjam sisi kanan
greeting.push_str("!"); // sama saja, dalam bentuk method
let mut tag = "kepiting".to_string(); // &str di kiri? jadikan String
tag += " #1";
println!("{greeting}");
println!("{tag}");
println!("{name} masih bisa dipakai");
}

Output:

Halo, Ferris!
kepiting #1
Ferris masih bisa dipakai
▶ Coba sendiri

Karena sisi kanan hanya dipinjam, name masih milikmu setelahnya. += dan push_str melakukan hal yang persis sama; pakai yang menurutmu lebih enak dibaca. Lihat String.

Perbaikan 4: Tambahkan bound T: AddAssign di kode generik

Section titled “Perbaikan 4: Tambahkan bound T: AddAssign di kode generik”

Pilih ini kalau error-nya bilang cannot be applied to type `T`. Di dalam fungsi generik, T bisa tipe apa saja, termasuk yang tidak punya +=. Bagian help dari rustc menyebutkan persis apa yang perlu ditambahkan: consider further restricting type parameter `T` with trait `AddAssign` (coba batasi parameter tipe T dengan trait AddAssign).

use std::ops::AddAssign;
fn total<T>(items: &[T]) -> T
where
T: Copy + Default + AddAssign,
{
let mut sum = T::default();
for &item in items {
sum += item;
}
sum
}
fn main() {
println!("{}", total(&[1, 2, 3]));
println!("{}", total(&[0.5, 0.25]));
}

Output:

6
0.75
▶ Coba sendiri

Sekarang total bisa dipakai untuk tipe apa pun yang bisa disalin, punya nilai awal, dan mendukung +=: integer, float, dan juga Money milikmu begitu ia mengimplementasikan trait-trait itu. Untuk angka biasa, items.iter().sum() melakukan hal yang sama dalam satu baris. Lihat Generics.

Menghitung dengan HashMap. let count = counts.entry(word).or_insert(0); lalu count += 1; menghasilkan cannot be applied to type `&mut i32`. count adalah referensi, bukan angkanya. Saran rustc: “+= can be used on i32 if you dereference the left-hand side” (+= bisa dipakai pada i32 kalau sisi kirinya di-dereference). Perbaikannya: *count += 1;. Lihat HashMap.

Mengalikan struct dengan sebuah angka. p *= 2 butuh MulAssign<{integer}>, karena sisi kanannya angka, bukan Point lain. Perbaikannya: implementasikan trait-nya untuk tipe sisi kanan tersebut:

impl std::ops::MulAssign<i32> for Point {
fn mul_assign(&mut self, factor: i32) {
self.x *= factor;
self.y *= factor;
}
}

Total berjalan di dalam loop. total += item.price dengan price bertipe Money buatanmu. Perbaikan 1 membuatnya bisa jalan. Kalau kamu lebih suka menulis prices.iter().copied().sum(), itu butuh trait Sum, dan tanpanya kamu mendapat E0277 (“a value of type Money cannot be made by summing an iterator”, nilai Money tidak bisa dibuat dengan menjumlahkan iterator).

  • E0369: binary operation cannot be applied (operator tidak bisa dipakai). Masalah yang sama untuk +, ==, <, dan operator lain yang tidak mengisi nilai.
  • E0308: mismatched types (tipe tidak cocok). Ini yang kamu dapat untuk s += t dengan dua String (pinjam saja: s += &t).
  • E0277: the trait bound is not satisfied (trait tidak terpenuhi). += ada untuk tipe di kiri, tapi tidak dengan tipe di kanan ini, seperti cannot add-assign `{float}` to `{integer}` untuk total += 2.5 saat total adalah integer.
  • E0384: cannot assign twice (diisi dua kali). x += 1 pada variabel yang dideklarasikan tanpa mut. Operatornya tidak masalah, variabelnya saja yang tidak mutable.
  • Semua error compiler: daftar lengkap halaman error.

Kuis singkat

1. Point mengimplementasikan Add, tapi p += q tetap menghasilkan E0368. Kenapa?

2. count berasal dari map.entry(word).or_insert(0). Baris mana yang bisa dikompilasi?

3. s dan t sama-sama String. Bagaimana cara menambahkan t ke s?

Skor: 0 / 3