if let dan while let

Ergonomi pencocokan pola tunggal tanpa match penuh.

Daftar isi

Topik ini membahas if let dan while let, dua bentuk sintaks yang memadukan pencocokan pola dengan kendali alur untuk kasus yang hanya memerlukan satu pola. Pembahasan mencakup pemakaian cabang else, perantaian else if let, serta pengulangan yang berjalan selama polanya cocok. Setelah membaca topik ini, pencocokan satu variant tidak lagi memerlukan match penuh dengan lengan _ yang kosong, sehingga kode untuk kasus umum seperti memeriksa Option menjadi lebih ringkas dan tetap mudah dibaca.

if let untuk satu pola#

Bentuk if let pola = nilai { ... } menjalankan bloknya hanya apabila pola cocok dengan nilai. Konstruksi ini setara dengan match yang hanya memiliki satu lengan berisi pola yang dikehendaki dan satu lengan _ yang tidak melakukan apa-apa. Keuntungannya bukan sekadar ringkas, melainkan juga pada kejelasan niat. Pembaca langsung tahu bahwa hanya satu bentuk nilai yang menarik perhatian dan sisanya memang diabaikan. Cabang else dijalankan ketika pola tidak cocok, sehingga penanganan kasus sisa tetap tersedia tanpa match penuh.

rust pengaturan-if-let.rs
struct Pengaturan {
    nama: String,
    nilai: Option<i32>,
}

impl Pengaturan {
    fn tampilkan(&self) {
        if let Some(nilai) = self.nilai {
            println!("{} = {}", self.nama, nilai);
        } else {
            println!("{} belum diatur", self.nama);
        }
    }
}

fn main() {
    let ukuran = Pengaturan {
        nama: String::from("ukuran_font"),
        nilai: Some(14),
    };
    let tema = Pengaturan {
        nama: String::from("tema"),
        nilai: None,
    };

    ukuran.tampilkan();
    tema.tampilkan();
}
text Keluaran
ukuran_font = 14
tema belum diatur

Metode tampilkan memeriksa dua pengaturan dengan bentuk nilai yang berbeda. Pola Some(nilai) melepaskan isi Option ke dalam nama nilai sekaligus menjadi syarat bloknya dijalankan, sedangkan None mengalir ke cabang else. Dibandingkan match, tidak ada lengan None => () yang harus ditulis hanya demi memenuhi kelengkapan pencocokan. Nama nilai juga siap dipakai langsung di dalam blok tanpa pemanggilan metode pembungkus seperti unwrap, karena pelepasan isi Some sudah dilakukan oleh polanya.

Rantai else if let#

Cabang else boleh berisi if let lain, sehingga beberapa pola dapat diperiksa berurutan dalam satu konstruksi. Bentuk lengkapnya dapat memadukan if let, else if, dan else if let sebagaimana perantaian if biasa, dan cabang else terakhir menangkap semua sisa. Pemeriksaan berhenti pada cabang pertama yang polanya cocok, sama seperti urutan lengan pada match.

rust rantai-else-if-let.rs
enum Menu {
    Nasi,
    Mie,
    Roti,
}

fn main() {
    let daftar_pesanan = [Some(Menu::Nasi), Some(Menu::Mie), Some(Menu::Roti), None];

    for pesanan in daftar_pesanan {
        if let Some(Menu::Nasi) = pesanan {
            println!("Memasak nasi");
        } else if let Some(Menu::Mie) = pesanan {
            println!("Memasak mie");
        } else if let Some(Menu::Roti) = pesanan {
            println!("Memanggang roti");
        } else {
            println!("Tidak ada pesanan yang dikenali");
        }
    }
}
text Keluaran
Memasak nasi
Memasak mie
Memanggang roti
Tidak ada pesanan yang dikenali

Pola Some(Menu::Nasi) memperlihatkan bahwa pola boleh bertingkat. Dua tingkat itu harus cocok sekaligus, yaitu nilai harus berupa Some dan isi dalamnya harus variant Nasi, sehingga cabang else menangkap sisa kemungkinan, yakni Some yang memuat variant Menu lain maupun None. Perantaian seperti ini nyaman untuk beberapa pola yang masing-masing berdiri sendiri, tetapi pada kasus dengan banyak variant, match tetap lebih ringkas karena tidak mengulang kata kunci else if let di setiap cabang. Sebagai pedoman praktis, rantai dengan satu atau dua cabang pola masih nyaman dibaca, sedangkan tiga cabang pola atau lebih sebaiknya ditulis sebagai match yang lengannya berjajar dalam satu blok. Pilihan di antara kedua bentuk tersebut didasarkan pada keterbacaan, bukan pada aturan bahasa, karena keduanya menghasilkan perilaku yang setara.

while let untuk pengulangan#

Bentuk while let pola = nilai { ... } mengevaluasi nilainya di setiap awal iterasi, menjalankan blok apabila polanya cocok, dan berhenti begitu pola tidak lagi cocok. Konstruksi ini cocok untuk sumber nilai yang perlahan habis, misalnya pop pada vector yang mengembalikan Option dan akhirnya None ketika datanya sudah terkuras.

rust antrean-while-let.rs
fn main() {
    let mut nomor_antrean = Vec::new();
    nomor_antrean.push(31);
    nomor_antrean.push(32);
    nomor_antrean.push(33);

    while let Some(nomor) = nomor_antrean.pop() {
        println!("Memproses nomor antrean {}", nomor);
    }

    println!("Antrean selesai");
}
text Keluaran
Memproses nomor antrean 33
Memproses nomor antrean 32
Memproses nomor antrean 31
Antrean selesai

Keluaran berurutan dari 33 ke 31 karena pop mengambil elemen dari ujung belakang vector. Setiap pemanggilan mengembalikan Some(nomor) selama masih ada isi, dan iterasi berhenti pada None tanpa perlu pemeriksaan is_empty terpisah. Variabel nomor_antrean harus mut karena pop meminjamnya sebagai &mut self, dan nama nomor diikat ulang pada setiap iterasi sehingga dapat dipakai di dalam blok tanpa dideklarasikan di luar. Ketika sumber nilainya bukan Option, pola apa pun yang bisa gagal tetap sah dipakai, dan pengulangan berhenti pada kegagalan pertama pola itu.

Tanpa while let, pola kerja yang sama harus ditulis dengan loop yang memeriksa hasil pop secara manual lalu memanggil break ketika mendapat None. Bentuk manual itu memuat dua titik keputusan yang terpisah, yaitu kondisi keluar dan pelepasan isi Some, sehingga kesalahan kecil seperti menukar urutan keduanya menghasilkan pengulangan yang salah. while let menyatukan kedua keputusan itu menjadi satu baris kondisi yang terbaca dari kiri ke kanan. Konstruksi ini juga menjadi dasar pola iterator yang memproses data sampai habis, karena sumber nilai yang berulang kali mengembalikan Option adalah bentuk umum pada pustaka standar.

Kesalahan umum#

Kesalahan pertama adalah memakai pola yang selalu cocok pada if let. Pola seperti nama variabel polos angka menerima nilai apa pun, sehingga kompilator memberi peringatan irrefutable if-let pattern dengan keterangan bahwa pola itu selalu cocok dan if let menjadi sia-sia, disertai saran menggantinya dengan let biasa. Sebaliknya, pola pada let biasa harus selalu cocok, sehingga penulisan let Some(x) = nilai_option ditolak karena pola itu bisa gagal. Aturan ini disebut keterrefutasan (refutability). Pola yang boleh gagal hanya sah pada if let, while let, dan lengan match, sehingga bentuk kendali alur dipilih menurut sifat polanya dan bukan menurut panjang penulisannya.

Kesalahan kedua adalah mengubah keadaan program di dalam blok while let tanpa memastikan sumber nilai yang dicocokkan menuju kegagalan pola. Apabila ekspresi yang dicocokkan selalu menghasilkan nilai yang cocok dengan polanya, pengulangan tidak akan pernah berhenti. Pastikan setiap iterasi menggerakkan sumber nilai menuju kondisi akhir, misalnya memastikan pop dipanggil pada vector yang isinya terbatas.

Ringkasan#

  • if let menjalankan bloknya ketika satu pola cocok, dengan cabang else untuk nilai yang tidak cocok.
  • Rantai else if let memeriksa beberapa pola berurutan, dan polanya boleh bertingkat seperti Some(Menu::Nasi).
  • while let mengulang selama polanya terus cocok dan berhenti pada kegagalan pertama, nyaman untuk sumber nilai yang habis seperti pop.
  • Pola if let dan while let harus bisa gagal, sedangkan pola let biasa harus selalu berhasil.

Referensi#