Sejak section Dasar Sintaks kita memakai +, -, dan * pada angka seolah operator tersebut adalah fitur bawaan bahasa yang tak bisa disentuh. Bagi tipe buatan sendiri, operator yang sama menolak bekerja: milimeter_a + milimeter_b pada struct kita gagal dikompilasi. Kenyataannya, tidak ada operator yang benar-benar “bawaan” di Rust — setiap operator adalah panggilan method yang menyamar, dan method itu milik trait keluarga std::ops. Operator + mencari method add dari trait Add, operator * mencari mul dari trait Mul, operator negasi - (di depan nilai) mencari neg dari trait Neg. Dengan demikian, memberi tipe kita kemampuan berpartisipasi dalam operator — operator overloading — sama sekali bukan fitur bahasa tersendiri, melainkan sekadar mengimplementasikan trait yang tepat, persis seperti delapan chapter sebelumnya. Chapter penutup section ini mempraktikkan hal itu pada tipe satuan panjang Milimeter.

Operator yang Menolak Tipe Buatan #

Mari kita mulai dari penolakannya sendiri. Struct Milimeter membungkus angka u32 — ketebalan benda dalam milimeter — dan tumpukan dua benda semestinya bisa dijumlahkan:

rust
struct Milimeter(u32);

fn main() {
    let tebal_buku = Milimeter(22);
    let tebal_laptop = Milimeter(18);
    let total = tebal_buku + tebal_laptop;

    println!("Total ketebalan: {} mm", total.0);
}

Pesan error kompilator:

text
error[E0369]: cannot add `Milimeter` to `Milimeter`
 --> main.rs:6:28
  |
6 |     let total = tebal_buku + tebal_laptop;
  |                 ---------- ^ ------------ Milimeter
  |                 |
  |                 Milimeter
  |
note: an implementation of `Add` might be missing for `Milimeter`
 --> main.rs:1:1
  |
1 | struct Milimeter(u32);
  | ^^^^^^^^^^^^^^^^ must implement `Add`
note: the trait `Add` must be implemented
 --> /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/core/src/ops/arith.rs:76:0

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0369`.

Pesan E0369 membongkar penyamaran operator dengan gamblang: judulnya menyatakan “tidak dapat menjumlahkan Milimeter dengan Milimeter”, dan catatannya menyebut pelaku di baliknya — implementasi trait Add yang hilang. Catatan kedua bahkan mengarah ke berkas sumber pustaka standar tempat trait Add didefinisikan (alamat lengkapnya bergantung pada versi rustc yang membangun program). Kesimpulan mekanismenya tegas: penulisan tebal_buku + tebal_laptop diturunkan kompilator menjadi pemanggilan method milik trait Add, dan karena Milimeter belum mengimplementasikan trait tersebut, penurunan gagal sebelum program jalan.

Mengimplementasikan Add untuk Milimeter #

Trait Add dari modul std::ops meminta dua hal: associated type bernama Output yang menyatakan tipe hasil penjumlahan, dan method add yang menerima dua operan. Kita tuliskan implementasinya untuk Milimeter:

rust
use std::ops::Add;

#[derive(Debug, Copy, Clone)]
struct Milimeter(u32);

impl Add for Milimeter {
    type Output = Milimeter;

    fn add(self, rhs: Milimeter) -> Milimeter {
        Milimeter(self.0 + rhs.0)
    }
}

fn main() {
    let book = Milimeter(22);
    let laptop = Milimeter(18);
    let mousepad = Milimeter(3);

    let stack = book + laptop + mousepad;

    println!("Tumpukan total: {} mm", stack.0);
    println!("Nilai asli tetap bisa dipakai: buku {} mm, laptop {} mm", book.0, laptop.0);
    println!("Bentuk debug: {:?}", stack);
}

Output yang Dihasilkan:

text
Tumpukan total: 43 mm
Nilai asli tetap bisa dipakai: buku 22 mm, laptop 18 mm
Bentuk debug: Milimeter(43)

Operator + kini hidup pada tipe buatan kita. Mari kita bedah baris demi baris:

  1. Baris 1 (use std::ops::Add;): trait Add dibawa ke scope sebelum digunakan. Tanpa baris ini, blok impl berikutnya ditolak karena trait-nya tidak dikenal — syarat scope yang sudah kita bahas pada chapter Implementasi Trait.
  2. Baris 3 (#[derive(Debug, Copy, Clone)]): tiga kemampuan turunan menyertai tipe. Copy berperan penting di contoh ini: method add mengambil operan secara kepemilikan (self dan rhs tanpa &), sehingga tanpa Copy, pemakaian book dan laptop pada baris 19 setelah penjumlahan akan ditolak E0382. Dengan Copy, nilai 4 byte milik Milimeter tersalin implisit saat dilepas — koneksi langsung ke materi derive pada chapter sebelumnya.
  3. Baris 6 (impl Add for Milimeter {): bentuk impl yang sudah akrab — trait Add (asing, dari std) diimplementasikan untuk tipe Milimeter (lokal). Susunan ini sah menurut orphan rule karena tipe penerima milik kita.
  4. Baris 7 (type Output = Milimeter;): associated type — deklarasi tipe yang menempel pada trait dan diisi oleh tiap implementasi. Add memakai Output agar satu trait bisa melayani penjumlahan dengan bentuk hasil berbeda-beda; kita mengisinya Milimeter karena menjumlah milimeter memang seharusnya menghasilkan milimeter, bukan angka telanjang.
  5. Baris 9 (fn add(self, rhs: Milimeter) -> Milimeter): method inti yang dicari operator +. Parameter pertama self berupa nilai kepemilikan, parameter kedua rhs (singkatan right-hand side, sisi kanan) bertipe Milimeter — tipe operan kanan ditentukan implementasi ini.
  6. Baris 10 (Milimeter(self.0 + rhs.0)): isi pekerjaannya sederhana — angka dalam u32 dijumlahkan, lalu dibungkus kembali menjadi Milimeter. Membungkus hasil (bukan mengembalikan u32) menjaga satuan tetap menempel pada nilai, sehingga salah satuan tertangkap di tipe, bukan terpapar di angka.
  7. Baris 19 (let stack = book + laptop + mousepad;): operator + dapat dirantai. Ekspresi ini diturunkan bertahap menjadi (book + laptop) + mousepad — dua pemanggilan add berurutan — dan tetap diperiksa tipe pada setiap tahap.

Keluarga Trait std::ops #

Add hanyalah satu penghuni modul std::ops. Peta keluarganya membentuk pola yang sama — satu trait per operator, satu method inti per trait:

OperatorTraitMethod intiContoh pemakaian
+Addaddbook + laptop
-Subsubtotal - book
*Mulmulprice * qty
/Divdivtotal / 3
- (di depan)Negneg-offset
+=AddAssignadd_assignstock += qty
[]Indexindexdaftar[0]

Tiga pengamatan layak dicatat dari tabel ini. Pertama, += bukan bagian dari Add — operator penugasan gabungan mencari trait AddAssign sendiri, sehingga tipe yang ingin mendukung keduanya perlu dua implementasi. Kedua, pengindeksan [] juga anggota keluarga ini lewat Index, meski perannya lebih sering di pustaka koleksi daripada kode aplikasi. Ketiga, Mul dan Add pada tipe u32 hanyalah implementasi standar dari trait yang sama yang kita implementasikan sendiri — angka bawaan dan tipe buatan berdiri di landasan yang setara.

Ringkasan #

Operator aritmetika di Rust adalah trait std::ops yang menyamar: + menuntut method add milik Add, dan tipe buatan tanpa implementasi ditolak dengan E0369 yang menyebut trait yang kurang. Menghidupkan operator berarti menulis impl Add for Milimeter dengan associated type Output menentukan tipe hasil dan method add menerima kedua operan — kontrak biasa yang kini kita penuhi dengan disiplin penuh setelah sembilan chapter berlatih. Derive Copy membuat operan bertahan setelah dijumlahkan, perantaian book + laptop + mousepad dipecah menjadi pemanggilan add bertahap, dan seluruh keluarga std::ops — dari Sub hingga Index — menunggu dengan pola serupa untuk kebutuhan berikutnya. Section Traits kita tutup di sini; perjalanan berlanjut ke topik yang menjaga referensi tetap aman sepanjang pemakaian: lifetime.