Match Expression — Rust

Match Expression di Rust match adalah fitur pattern matching yang sangat powerful di Rust — mirip switch di bahasa lain, tapi jauh lebih ekspresif. Sintaks…

match membandingkan satu nilai dengan daftar pola (pattern) dari atas ke bawah, lalu menjalankan cabang pertama yang cocok. Bayangin papan tarif ongkir di meja kasir Kedai Martabak Bang Rusdi. Tiap total belanja jatuh ke tepat satu baris tarif: di bawah Rp50.000 ongkirnya Rp10.000, sampai Rp99.999 ongkirnya Rp5.000, dan Rp100.000 ke atas gratis. Bang Rusdi gak mau papan yang bolong: kalau ada total yang gak punya baris, papan itu gak boleh dipasang. Total belanja itu nilai yang di-match, tiap baris tarif itu satu cabang, dan aturan "gak boleh bolong" itu yang disebut exhaustive.

🧪 Tebakan cepat: kasir menulis papan ongkirnya tanpa baris terakhir:

let ongkir = match total {
    0..=49_999 => 10000,
    50_000..=99_999 => 5000,
};

Dengan total 70000, apakah programnya mencetak 5000? Tebak dulu, jawabannya ada di bagian "Harus mencakup semua kemungkinan".

match: satu nilai, banyak cabang

fn harga(menu: &str) -> u32 {
    match menu {
        "martabak manis" => 32000,
        "martabak telur" => 38000,
        "es teh" | "es jeruk" => 5000,
        _ => 0,
    }
}

fn main() {
    println!("{}", harga("martabak telur"));
    println!("{}", harga("es jeruk"));
    println!("{}", harga("kopi"));
}
38000
5000
0

Range dan beberapa pola

Untuk angka, pola bisa berupa range dengan ..=, sama seperti di for:

fn main() {
    let total: u32 = 32000 * 2 + 38000 * 1;
    let ongkir = match total {
        0..=49_999 => 10000,
        50_000..=99_999 => 5000,
        _ => 0,
    };
    println!("Total: {}, ongkir: {}", total, ongkir);
}

Output-nya Total: 102000, ongkir: 0.

total Cabang yang cocok ongkir
32000 0..=49_999 10000
70000 50_000..=99_999 5000
102000 _ 0

Harus mencakup semua kemungkinan

Ini papan ongkir tebakan tadi, tanpa cabang _:

error[E0004]: non-exhaustive patterns: `100000_u32..=u32::MAX` not covered
 --> src/main.rs:3:24
  |
3 |     let ongkir = match total {
  |                        ^^^^^ pattern `100000_u32..=u32::MAX` not covered
  |
  = note: the matched value is of type `u32`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
  |
5 ~         50_000..=99_999 => 5000,
6 ~         100000_u32..=u32::MAX => todo!(),
  |

Compiler menghitung sendiri angka mana yang belum punya cabang: 100000 sampai angka u32 terbesar. Karena compile gagal, gak ada yang tercetak, termasuk untuk total 70000. Itu jawaban tebakannya: gak, programnya gak jalan sama sekali.

match menghasilkan nilai

Semua cabang match yang hasilnya disimpan harus setipe, persis seperti if:

error[E0308]: `match` arms have incompatible types
 --> src/main.rs:5:14
  |
3 |       let ongkir = match total {
  |  __________________-
4 | |         0..=99_999 => 10000,
  | |                       ----- this is found to be of type `{integer}`
5 | |         _ => "gratis",
  | |              ^^^^^^^^ expected integer, found `&str`
6 | |     };
  | |_____- `match` arms have incompatible types

Cabang pertama angka, jadi "gratis" ditolak. Simpan 0, lalu tulis kata "gratis" saat mencetak.

Guard dan binding @

Kadang range saja gak cukup. Guard menambah syarat if di sebuah cabang, dan nama @ pola menyimpan nilai yang cocok ke variabel:

fn cek_pesanan(porsi: u32) {
    match porsi {
        0 => println!("Belum ada pesanan"),
        n @ 1..=5 => println!("{} porsi, langsung dimasak", n),
        n if n > 20 => println!("{} porsi? Telepon dulu pembelinya", n),
        n => println!("{} porsi, masuk antrean", n),
    }
}

fn main() {
    cek_pesanan(0);
    cek_pesanan(2);
    cek_pesanan(8);
    cek_pesanan(25);
}
Belum ada pesanan
2 porsi, langsung dimasak
8 porsi, masuk antrean
25 porsi? Telepon dulu pembelinya

🎯 Sekilas: match dengan Option

match paling sering dipakai untuk nilai yang mungkin ada, mungkin gak. harga.get(3) pada array tiga harga gak menemukan apa-apa:

fn main() {
    let harga = [32000, 38000, 5000];
    match harga.get(3) {
        Some(h) => println!("Harga: {}", h),
        None => println!("Menu nomor 3 gak ada"),
    }
}

Output-nya Menu nomor 3 gak ada. Some(h) berarti ada isinya (disimpan di h), None berarti kosong. Tipe ini namanya Option, dibahas lengkap di pelajaran Result dan Option. Kamu bakal ketemu lagi di Vec dan HashMap.

⚠️ Jebakan yang sering ketemu

TL;DR: match nilai { pola => hasil, ... } mengecek pola dari atas, dan yang pertama cocok menang. Pola bisa angka, teks, range 0..=49_999, beberapa pola dengan |, guard n if n > 20, atau binding n @ 1..=5. _ menangkap semua yang belum tertangkap. match wajib exhaustive: semua kemungkinan nilai harus punya cabang, kalau gak compile gagal dengan E0004 non-exhaustive patterns. Teks selalu butuh _, angka bisa ditutup dengan range. match adalah expression, jadi semua cabangnya harus setipe dan hasilnya bisa disimpan: let ongkir = match total { ... };.

Yang akan kamu pelajari