Traits di Rust
Trait adalah cara mendefinisikan perilaku bersama yang bisa diimplementasikan oleh berbagai tipe — mirip interface di Java atau protocol di Swift, tapi dengan kemampuan lebih.
Mendefinisikan Trait
trait Ringkas {
fn ringkasan(&self) -> String;
}
Trait ini berkata: "tipe apa pun yang mengimplementasikan Ringkas harus punya method ringkasan yang return String".
Implementasi Trait untuk Tipe
struct Artikel {
judul: String,
penulis: String,
isi: String,
}
impl Ringkas for Artikel {
fn ringkasan(&self) -> String {
format!("{}, oleh {}", self.judul, self.penulis)
}
}
struct Tweet {
user: String,
konten: String,
}
impl Ringkas for Tweet {
fn ringkasan(&self) -> String {
format!("@{}: {}", self.user, self.konten)
}
}
Default Method Implementation
Trait bisa menyediakan implementasi default — tipe yang meng-implement bisa memilih memakai default atau override:
trait Sapa {
fn nama(&self) -> String;
// Default implementation
fn halo(&self) -> String {
format!("Halo, {}!", self.nama())
}
}
struct Orang { nama: String }
impl Sapa for Orang {
fn nama(&self) -> String {
self.nama.clone()
}
// halo() pakai default
}
Trait sebagai Parameter
fn umumkan(item: &impl Ringkas) {
println!("BARU: {}", item.ringkasan());
}
// Bentuk panjangnya pakai generic + trait bound
fn umumkan_v2<T: Ringkas>(item: &T) {
println!("BARU: {}", item.ringkasan());
}
Multiple Trait Bounds
use std::fmt::Display;
fn tampilkan<T: Display + Clone>(item: T) {
let copy = item.clone();
println!("{} {}", item, copy);
}
Standard Traits dengan derive
Beberapa trait penting dari standard library bisa di-derive otomatis:
#[derive(Debug, Clone, PartialEq)]
struct Produk {
nama: String,
harga: f64,
}
let p1 = Produk { nama: "Buku".into(), harga: 50000.0 };
let p2 = p1.clone(); // dari Clone
println!("{:?}", p1); // dari Debug
println!("{}", p1 == p2); // dari PartialEq
Trait Objects — Polymorphism Dinamis
Jika butuh menyimpan berbagai tipe yang implement trait yang sama dalam satu koleksi, pakai &dyn Trait atau Box<dyn Trait>:
let items: Vec<Box<dyn Ringkas>> = vec![
Box::new(Artikel { /* .. */ }),
Box::new(Tweet { /* .. */ }),
];
for item in &items {
println!("{}", item.ringkasan());
}
Trade-off: dyn Trait pakai dynamic dispatch (sedikit lebih lambat). Generic pakai static dispatch (cepat, tapi binary lebih besar).