Rantai else if yang panjang cepat terasa tidak nyaman ketika tujuannya sederhana: memetakan satu nilai ke satu tindakan. Aplikasi kasir memetakan kode menu 1, 2, 3 ke nama masakan; aplikasi akademik memetakan rentang nilai ke predikat; sistem jadwal memetakan nomor hari ke kategori hari kerja atau akhir pekan. Rust menyediakan konstruksi khusus untuk pemetaan semacam ini bernama match — pencocokan pola (pattern matching) yang membandingkan satu nilai terhadap serangkaian pola dan menjalankan cabang dari pola pertama yang cocok. Dua sifat match membuatnya istimewa: pemeriksaan berurutan dari atas ke bawah, dan jaminan compiler bahwa seluruh kemungkinan nilai sudah tertangani.
Sintaks Dasar match #
Bentuk match dimulai dari kata kunci match, diikuti nilai yang ingin diperiksa, lalu blok berisi cabang-cabang (arm) dengan format pola => ekspresi. Setiap cabang dipisahkan tanda koma. Rust membandingkan nilai dengan setiap pola dari cabang paling atas; cabang pertama yang polanya cocok dieksekusi, dan seluruh cabang di bawahnya diabaikan. Mari kita mulai dengan pemetaan kode menu warung:
fn main() {
let menu_code = 2;
match menu_code {
1 => println!("Anda memesan Nasi Goreng."),
2 => println!("Anda memesan Mie Ayam."),
3 => println!("Anda memesan Sate Ayam."),
_ => println!("Kode menu tidak dikenal."),
}
}Output yang Dihasilkan:
Anda memesan Mie Ayam.Mari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 2 (
let menu_code = 2;): variabelmenu_codemenyimpan kode pilihan pelanggan dengan nilai2. - Baris 4 (
match menu_code {): Rust mulai membandingkan nilaimenu_codedengan pola-pola di setiap cabang, mulai dari cabang paling atas. - Baris 5 (
1 => println!("Anda memesan Nasi Goreng."),): pola1dibandingkan dengan nilai2dan tidak cocok, sehingga cabang ini dilewati. - Baris 6 (
2 => println!("Anda memesan Mie Ayam."),): pola2cocok dengan nilai2, sehingga ekspresi di sisi kanan tanda panah dieksekusi dan mencetak pesan pesanan. - Baris 7–8 (
3 => ...dan_ => ...): kedua cabang ini tidak pernah diperiksa lagi karena sudah ada cabang yang terpenuhi; tanda koma pada akhir baris menandai batas tiap cabang.
Perhatikan _ pada cabang terakhir — pola wildcard yang cocok dengan nilai apa pun. Perannya serupa dengan else pada rantai if: menampung semua nilai yang tidak tertangkap cabang lain. Arah tanda => juga mudah diingat: nilai di kiri “mengarah ke” hasil di kanan.
match Harus Mencakup Semua Kemungkinan #
Sifat paling khas match adalah pemeriksaan exhaustive: seluruh kemungkinan nilai dari tipe yang diperiksa harus terwakili oleh gabungan pola. Compiler menolak match yang masih menyisakan kemungkinan nilai yang tak tertangani. Mari kita hapus cabang _ dari program menu dan lihat reaksinya:
fn main() {
let menu_code = 2;
match menu_code {
1 => println!("Anda memesan Nasi Goreng."),
2 => println!("Anda memesan Mie Ayam."),
3 => println!("Anda memesan Sate Ayam."),
}
}Output yang Dihasilkan (galat kompilasi):
error[E0004]: non-exhaustive patterns: `i32::MIN..=0_i32` and `4_i32..=i32::MAX` not covered
--> main.rs:4:11
|
4 | match menu_code {
| ^^^^^^^^^ patterns `i32::MIN..=0_i32` and `4_i32..=i32::MAX` not covered
|
= note: the matched value is of type `i32`
error: aborting due to 1 previous errorPesan galat tersebut sangat informatif: karena menu_code bertipe i32, compiler menghitung bahwa nilai i32::MIN sampai 0 dan 4 sampai i32::MAX tidak tertangani oleh ketiga cabang yang ada. Program tidak hanya diberi tahu salah, tetapi juga diberi tahu kekurangannya apa. Menambahkan cabang _ => ... atau pola rentang yang melengkapi celah akan membuat kompilasi lolos.
match sebagai Ekspresi #
Sama seperti loop, sebuah match adalah expression yang menghasilkan nilai. Nilai hasilnya berasal dari cabang yang terpenuhi, sehingga setiap cabang harus menghasilkan tipe yang sama. Kemampuan ini memungkinkan kita mengikat hasil match langsung ke variabel tanpa pernyataan penugasan di dalam tiap cabang:
fn main() {
let menu_code = 3;
let menu_name = match menu_code {
1 => "Nasi Goreng",
2 => "Mie Ayam",
3 => "Sate Ayam",
_ => "Menu tidak dikenal",
};
println!("Anda memesan {}.", menu_name);
}Output yang Dihasilkan:
Anda memesan Sate Ayam.Mari kita bedah program di atas:
- Baris 4 (
let menu_name = match menu_code {): hasil dari keseluruhanmatchakan diikat ke variabelmenu_name; cabang mana yang menyumbang nilai baru ditentukan saat pencocokan berjalan. - Baris 5–8 (
1 => "Nasi Goreng", ...): setiap cabang kini berupa ekspresi string&str, bukan statementprintln!— keempat cabang harus bertipe sama agarmatchmenghasilkan satu tipe yang pasti. - Baris 7 (
3 => "Sate Ayam",): nilaimenu_codeadalah3, sehingga cabang ini yang menyumbang nilai"Sate Ayam"sebagai hasilmatch. - Baris 9 (
};): titik koma setelah kurung kurawal menutup statementlet; perhatikan pola ini sama denganlet result = loop { ... };pada Chapter loop Tak Berhingga. - Baris 11 (
println!("Anda memesan {}.", menu_name);): variabelmenu_namesudah berisi nama menu sehingga percetakan cukup dilakukan sekali di luarmatch.
Perubahan kecil ini memisahkan dua keputusan: match bertugas memilih nilai, dan println! bertugas menampilkan nilai. Pemisahan tanggung jawab semacam ini membuat cabang-cabang mudah diuji dan digunakan ulang karena tidak terikat aksi cetak.
Pola Gabungan dan Rentang Angka #
Dua bentuk pola tambahan membuat match semakin ringkas. Pola gabungan memakai tanda pipa | untuk menyatukan beberapa nilai dalam satu cabang, dan pola rentang memakai ..= untuk menangkap interval nilai. Program berikut menggabungkan keduanya pada pengelompokan nomor hari:
fn main() {
let day = 7;
match day {
1 | 2 | 3 | 4 | 5 => println!("Hari {} adalah hari kerja.", day),
6 | 7 => println!("Hari {} adalah akhir pekan.", day),
_ => println!("{} bukan nomor hari yang sah.", day),
}
}Output yang Dihasilkan:
Hari 7 adalah akhir pekan.Cabang pertama cocok untuk nilai 1 sampai 5 tanpa perlu menulis lima cabang terpisah. Nilai 7 melewati cabang pertama, cocok dengan pola 6 | 7, dan mencetak keterangan akhir pekan. Rentang nilai berkelanjutan lebih ringkas lagi dengan ..=, sebagaimana pemetaan skor ujian ke predikat berikut:
fn main() {
let score = 78;
let grade = match score {
90..=100 => "A",
80..=89 => "B",
70..=79 => "C",
60..=69 => "D",
0..=59 => "E",
_ => "Nilai tidak sah",
};
println!("Predikat untuk skor {} adalah {}.", score, grade);
}Output yang Dihasilkan:
Predikat untuk skor 78 adalah C.Mari kita uraikan kedua contoh di atas:
- Baris 5 pada program hari (
1 | 2 | 3 | 4 | 5 => ...): tanda pipa menyatakan “atau” — cabang terpenuhi apabila nilai cocok dengan salah satu dari lima angka tersebut; nilai7tidak termasuk sehingga pencocokan turun ke cabang berikutnya. - Baris 6 pada program hari (
6 | 7 => ...): pola6 | 7cocok dengan nilai7, sehingga cabang inilah yang dieksekusi dan mencetak keterangan akhir pekan. - Baris 4 pada program predikat (
let grade = match score {): hasilmatchdiikat ke variabelgrade; skor78dibandingkan dengan pola-pola rentang mulai dari cabang paling atas. - Baris 5 pada program predikat (
90..=100 => "A",): rentang inklusif menangkap seluruh nilai dari90hingga100dalam satu cabang; skor78tidak berada di dalamnya sehingga pencocokan turun ke cabang berikutnya. - Baris 7 pada program predikat (
70..=79 => "C",): skor78berada di dalam rentang ini, sehingga variabelgrademenerima nilai"C"dan pencocokan berhenti. - Baris 10 pada program predikat (
_ => "Nilai tidak sah",): cabang penutup tetap dibutuhkan karena tipei32memuat nilai di luar0..=100, misalnya skor negatif atau di atas100; tanpa cabang ini kompilasi gagal dengan galat non-exhaustive.
Ringkasan #
match membandingkan satu nilai terhadap serangkaian pola dari cabang paling atas dan menjalankan cabang pertama yang cocok. Compiler menjamin kecukupan pencakupan (exhaustive) dengan menolak match yang menyisakan kemungkinan nilai, dan pola _ menjadi penampung nilai sisanya. Pola gabungan 1 | 2 | 3 serta rentang 90..=100 memampatkan banyak kondisi menjadi satu cabang, dan match sebagai expression menghasilkan nilai yang dapat langsung diikat ke variabel. Kemampuan mengekspresikan nilai dari cabang kondisional akan kita perdalam pada Chapter berikutnya tentang if sebagai ekspresi.