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:
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:
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:
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:
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:
- Baris 1 (
use std::ops::Add;): traitAdddibawa 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. - Baris 3 (
#[derive(Debug, Copy, Clone)]): tiga kemampuan turunan menyertai tipe.Copyberperan penting di contoh ini: methodaddmengambil operan secara kepemilikan (selfdanrhstanpa&), sehingga tanpaCopy, pemakaianbookdanlaptoppada baris 19 setelah penjumlahan akan ditolak E0382. DenganCopy, nilai 4 byte milikMilimetertersalin implisit saat dilepas — koneksi langsung ke materi derive pada chapter sebelumnya. - Baris 6 (
impl Add for Milimeter {): bentuk impl yang sudah akrab — traitAdd(asing, dari std) diimplementasikan untuk tipeMilimeter(lokal). Susunan ini sah menurut orphan rule karena tipe penerima milik kita. - Baris 7 (
type Output = Milimeter;): associated type — deklarasi tipe yang menempel pada trait dan diisi oleh tiap implementasi.AddmemakaiOutputagar satu trait bisa melayani penjumlahan dengan bentuk hasil berbeda-beda; kita mengisinyaMilimeterkarena menjumlah milimeter memang seharusnya menghasilkan milimeter, bukan angka telanjang. - Baris 9 (
fn add(self, rhs: Milimeter) -> Milimeter): method inti yang dicari operator+. Parameter pertamaselfberupa nilai kepemilikan, parameter keduarhs(singkatan right-hand side, sisi kanan) bertipeMilimeter— tipe operan kanan ditentukan implementasi ini. - Baris 10 (
Milimeter(self.0 + rhs.0)): isi pekerjaannya sederhana — angka dalamu32dijumlahkan, lalu dibungkus kembali menjadiMilimeter. Membungkus hasil (bukan mengembalikanu32) menjaga satuan tetap menempel pada nilai, sehingga salah satuan tertangkap di tipe, bukan terpapar di angka. - Baris 19 (
let stack = book + laptop + mousepad;): operator+dapat dirantai. Ekspresi ini diturunkan bertahap menjadi(book + laptop) + mousepad— dua pemanggilanaddberurutan — 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:
| Operator | Trait | Method inti | Contoh pemakaian |
|---|---|---|---|
+ | Add | add | book + laptop |
- | Sub | sub | total - book |
* | Mul | mul | price * qty |
/ | Div | div | total / 3 |
- (di depan) | Neg | neg | -offset |
+= | AddAssign | add_assign | stock += qty |
[] | Index | index | daftar[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.