Topik ini membahas match, yaitu ekspresi kendali alur yang mencocokkan sebuah nilai dengan rangkaian pola dan menjalankan lengan pertama yang cocok. Pembahasan mencakup syarat pencocokan yang wajib lengkap, binding variabel yang mengambil data dari pola, guard sebagai syarat tambahan, serta pola _ yang menangkap nilai sisanya. Setelah membaca topik ini, nilai enum dapat dibedah dengan aman karena kompilator menjamin tidak ada variant yang terlewat, dan percabangan berbasis isi data dapat ditulis tanpa pemeriksaan manual yang rawan salah.
Cara kerja pencocokan#
Ekspresi match ditulis dengan bentuk match nilai { pola => hasil, ... }. Setiap pasangan pola dan hasil disebut lengan (arm), dan pola diperiksa berurutan dari atas ke bawah begitu nilai masuk. Lengan pertama yang polanya cocok akan dijalankan, sedangkan lengan berikutnya tidak diperiksa lagi. Hasil setiap lengan berupa ekspresi, dan apabila isinya lebih dari satu pernyataan, lengan ditulis dengan blok kurung kurawal. Diagram berikut menggambarkan alur pemeriksaan tersebut.
Nilai yang paling sering dicocokkan adalah enum karena polanya dapat menelusuri variant beserta data di dalamnya. Program berikut memeringkat tiga status tugas menjadi satu kalimat ringkas.
enum StatusTugas {
BelumMulai,
Dikerjakan { persen: u8 },
Disetor { nilai: u8 },
}
fn ringkas(status: &StatusTugas) -> String {
match status {
StatusTugas::BelumMulai => String::from("belum mulai"),
StatusTugas::Dikerjakan { persen } => format!("dikerjakan, capaian {}%", persen),
StatusTugas::Disetor { nilai } => format!("disetor, nilai {}", nilai),
}
}
fn main() {
let papan = [
StatusTugas::BelumMulai,
StatusTugas::Dikerjakan { persen: 40 },
StatusTugas::Disetor { nilai: 92 },
];
for status in &papan {
println!("{}", ringkas(status));
}
}belum mulai
dikerjakan, capaian 40%
disetor, nilai 92Pada tiap lengan, nama seperti persen dan nilai adalah binding, yaitu nama variabel yang menerima bagian dari nilai yang dicocokkan. Pola StatusTugas::Dikerjakan { persen } cocok dengan setiap nilai variant Dikerjakan apa pun isinya, kemudian mengikat field persen ke nama yang sama sehingga angka itu dapat dipakai di hasil lengannya. Karena fungsi ringkas menerima referensi, binding di dalam pola juga berbentuk referensi yang dapat langsung dipakai pada format!. Seluruh lengan menghasilkan String, dan nilai seluruh ekspresi match adalah hasil lengan yang terpilih, sehingga match dapat dikembalikan langsung dari fungsi tanpa variabel penampung.
Pencocokan yang wajib lengkap#
Rust menuntut pola pada sebuah match mencakup semua kemungkinan nilai. Sifat ini disebut pencocokan lengkap (exhaustive matching), dan pelanggarannya ditolak dengan error E0004 sebelum program berjalan. Contoh berikut hanya menangani dua dari tiga variant.
export PATH="$HOME/.cargo/bin:$PATH"
# buat program dengan match yang tidak lengkap
printf 'enum StatusMesin {\n Aktif,\n Siaga,\n Mati,\n}\n\nfn main() {\n let status = StatusMesin::Siaga;\n match status {\n StatusMesin::Aktif => println!("mesin aktif"),\n StatusMesin::Mati => println!("mesin mati"),\n }\n}\n' > tidak-lengkap.rs
# kompilasi untuk melihat error E0004
rustc --edition=2021 tidak-lengkap.rs 2>&1 | sed -n '1,14p'error[E0004]: non-exhaustive patterns: `StatusMesin::Siaga` not covered
--> tidak-lengkap.rs:9:11
|
9 | match status {
| ^^^^^^ pattern `StatusMesin::Siaga` not covered
|
note: `StatusMesin` defined here
--> tidak-lengkap.rs:1:6
|
1 | enum StatusMesin {
| ^^^^^^^^^^^
2 | Aktif,
3 | Siaga,
| ----- not coveredPesan error tersebut menyebutkan variant mana yang belum tertangani, sehingga penambahan variant baru pada enum akan otomatis membuat setiap match lama yang belum menanganinya gagal dikompilasi. Sifat ini mengubah penambahan variant dari sumber bug tersembunyi menjadi daftar kerja yang eksplisit. Apabila nilai sisa memang tidak perlu dibedakan, pola _ dapat menutup kekurangan itu karena pola ini cocok dengan nilai apa pun dan tidak mengikat nama apa pun. Pola _ disebut juga wildcard, dan pada pencocokan bilangan pola ini lazim dipakai sebagai lengan terakhir.
Posisi _ menentukan hasil karena pemeriksaan berhenti pada kecocokan pertama. Pola _ yang ditulis sebelum pola lain membuat lengan di bawahnya tidak akan pernah terjangkau, dan kompilator memberi peringatan unreachable pattern berikut keterangan bahwa _ cocok dengan nilai apa pun.
// tidak-dijalankan
let angka = 7;
match angka {
_ => println!("nilai apa pun"),
7 => println!("tujuh"),
}Binding variabel dan guard#
Guard adalah syarat tambahan yang ditulis setelah pola dengan kata kunci if. Sebuah lengan hanya terpilih apabila polanya cocok dan guardnya bernilai benar, sehingga satu pola yang sama boleh muncul beberapa kali dengan guard yang berbeda. Guard dapat memakai variabel yang diikat oleh polanya sendiri, dan karena pemeriksaan berhenti pada kecocokan pertama, urutan lengan dengan guard menentukan hasil akhir.
Program berikut menggolongkan pembacaan sensor dengan dua lengan bermuatan guard untuk tipe data yang sama.
enum BacaSensor {
Suhu(f64),
Kelembapan(u8),
Error(String),
}
fn laporan(bacaan: BacaSensor) -> String {
match bacaan {
BacaSensor::Suhu(v) if v > 37.5 => format!("demam ({})", v),
BacaSensor::Suhu(v) => format!("normal ({})", v),
BacaSensor::Kelembapan(v) if v > 80 => format!("terlalu lembap ({})", v),
BacaSensor::Kelembapan(v) => format!("kelembapan aman ({})", v),
BacaSensor::Error(pesan) => format!("sensor bermasalah: {}", pesan),
}
}
fn main() {
let daftar = [
BacaSensor::Suhu(36.5),
BacaSensor::Suhu(38.2),
BacaSensor::Kelembapan(65),
BacaSensor::Error(String::from("kabel lepas")),
];
for bacaan in daftar {
println!("{}", laporan(bacaan));
}
}normal (36.5)
demam (38.2)
kelembapan aman (65)
sensor bermasalah: kabel lepasPembacaan 36.5 gagal pada guard v > 37.5 lalu jatuh ke lengan BacaSensor::Suhu(v) tanpa guard, sedangkan 38.2 berhenti pada lengan pertama. Binding v pada kedua lengan itu berdiri sendiri, sehingga guard pada lengan pertama tidak memengaruhi pengikatan v pada lengan kedua. Satu konsekuensi penting dari guard adalah pemeriksa kelengkapan tidak menghitungnya sebagai cakupan. Sebuah match yang seluruh lengannya berguard tetap ditolak dengan E0004 walaupun secara logika semua kemungkinan sudah terwakili, karena guard dapat bernilai salah saat program berjalan. Solusinya adalah menambahkan satu lengan tanpa guard untuk setiap pola yang tersisa.
Kesalahan umum#
Kesalahan pertama adalah membuat lengan yang hasilnya bertipe berbeda. Karena match adalah ekspresi bernilai tunggal, semua lengan harus menghasilkan tipe yang sama, dan pelanggarannya ditolak dengan error E0308 yang menampilkan tipe lengan pertama sebagai acuan. Misalnya sebuah lengan menghasilkan &str sedangkan lengan lain menghasilkan bilangan, seperti pola 1 => "satu" berdampingan dengan _ => 2. Pesan errornya menandai lengan pertama sebagai pemilik tipe acuan dan menunjuk lengan yang menyimpang, sehingga sumber ketidaksesuaian cepat ditemukan. Perbaikannya adalah menyamakan tipe seluruh lengan, misalnya mengubah keduanya menjadi String.
Kesalahan kedua adalah mengira guard dilibatkan dalam pemeriksaan kelengkapan seperti pola biasa. Penulisan Arah::Utara if kondisi dan Arah::Selatan if kondisi saja tetap memicu E0004 meskipun kedua variant sudah disebut, karena setiap guard dapat gagal saat program berjalan dan kompilator tidak mengevaluasi isinya. Pesan errornya menyebutkan seluruh pola yang belum tercakup sekaligus, sehingga letak lengan penutup yang harus ditambah langsung terlihat. Tambahkan satu lengan tanpa guard sebagai penutup agar kelengkapan terpenuhi.
Ringkasan#
matchmemeriksa pola secara berurutan dan menjalankan lengan pertama yang cocok, kemudian seluruh ekspresi bernilai hasil lengan itu.- Pencocokan wajib lengkap. Variant yang belum tertangani memicu error E0004, sehingga penambahan variant baru terdeteksi di semua lokasi pencocokan lama.
- Binding mengambil bagian dari nilai yang cocok ke dalam nama variabel yang dapat dipakai pada hasil lengan maupun guard.
- Guard menambah syarat
ifpada pola, tidak dihitung sebagai cakupan kelengkapan, dan pola_menangkap semua nilai sisa tanpa mengikat nama.
Referensi#
- The match Control Flow Construct — The Rust Programming Language — Dasar match, pencocokan lengkap, pola garis bawah, dan guard.
- Pattern Syntax — The Rust Programming Language — Ragam pola, binding, guard, binding at, dan pola bertingkat.