Generics di Rust — Rust

Generics di Rust Generics memungkinkan kita menulis kode yang bekerja untuk banyak tipe sekaligus, tanpa mengorbankan type safety. Kamu sudah pernah melihat…

Generics di Rust

Generics memungkinkan kita menulis kode yang bekerja untuk banyak tipe sekaligus, tanpa mengorbankan type safety. Kamu sudah pernah melihat generics: Vec<T> dan HashMap<K, V>!

Kenapa Generics?

Bayangkan kamu butuh fungsi terbesar untuk i32, lalu butuh juga untuk f64, lalu untuk char. Tanpa generics, kamu harus menulis 3 fungsi terpisah dengan logika identik. Dengan generics, cukup satu fungsi.

Generic Function

fn terbesar<T: PartialOrd>(list: &[T]) -> &T {
    let mut terbesar = &list[0];
    for item in list {
        if item > terbesar {
            terbesar = item;
        }
    }
    terbesar
}

fn main() {
    let angka = vec![10, 25, 3, 47, 8];
    let huruf = vec!['r', 'a', 'z', 'b'];
    println!("{}", terbesar(&angka));  // 47
    println!("{}", terbesar(&huruf));  // z
}

T adalah type parameter — placeholder untuk tipe apa pun. T: PartialOrd adalah trait bound yang mengatakan "T harus bisa dibandingkan".

Generic Struct

struct Titik<T> {
    x: T,
    y: T,
}

fn main() {
    let titik_int = Titik { x: 5, y: 10 };
    let titik_float = Titik { x: 1.5, y: 2.7 };
}

Multiple Type Parameters

struct Pasangan<K, V> {
    kunci: K,
    nilai: V,
}

let p = Pasangan { kunci: "umur", nilai: 25 };

Generic Method

impl<T> Titik<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

// Method hanya untuk Titik<f64>
impl Titik<f64> {
    fn jarak_dari_origin(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

Trait Bounds

Untuk menggunakan operasi pada tipe generic, compiler butuh jaminan tipe itu mendukung operasi tersebut — lewat trait bound:

use std::fmt::Display;

fn cetak<T: Display>(item: T) {
    println!("{}", item);
}

// Multiple bounds dengan +
fn cetak_dan_clone<T: Display + Clone>(item: T) {
    let salinan = item.clone();
    println!("{} {}", item, salinan);
}

where Clause — Lebih Bersih untuk Bound Kompleks

fn proses<T, U>(a: T, b: U) -> i32
where
    T: Display + Clone,
    U: Clone + std::fmt::Debug,
{
    println!("{} {:?}", a, b);
    0
}

Zero-Cost Abstraction

Generic di Rust tidak punya runtime cost. Compiler melakukan monomorphization — membuat versi konkret untuk setiap tipe yang dipakai saat compile time. Jadi secepat kode yang ditulis manual!

Yang akan kamu pelajari