Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Refutability (Keterbantahan): Apakah sebuah Pattern Bisa Gagal Cocok?

Patterns datang dalam dua bentuk: refutable (bisa dibantah/bisa gagal) dan irrefutable (tidak bisa dibantah/pasti sukses). Patterns yang bakal selalu cocok dengan kemungkinan nilai apa pun yang diberikan ke dia itu disebut irrefutable. Contohnya adalah x di dalam statement let x = 5; karena x cocok dengan apa aja dan oleh karena itu tidak mungkin gagal buat cocok. Patterns yang bisa aja gagal buat cocok dengan beberapa kemungkinan nilai itu disebut refutable. Contohnya adalah Some(x) di dalam ekspresi if let Some(x) = a_value karena kalau nilai di variabel a_value ternyata adalah None bukannya Some, maka pattern Some(x) itu tidak bakal cocok.

Parameter fungsi, statement let, dan loop for cuma bisa nerima patterns yang irrefutable karena programnya tidak bisa ngelakuin hal berguna apa pun kalau nilainya ternyata tidak cocok. Ekspresi if let dan while let serta statement let...else bisa nerima patterns yang refutable maupun irrefutable, tapi compiler bakal ngasih peringatan (warning) kalau kita memakai patterns yang irrefutable di sana. Hal ini karena, secara definisi, konstruk-konstruk tersebut memang ditujukan buat menangani kemungkinan gagal: fungsionalitas dari sebuah kondisional ada pada kemampuannya buat melakukan hal yang berbeda-beda tergantung pada apakah dia sukses atau gagal.

Secara umum, kita tidak perlu pusing mikirin perbedaan antara patterns yang refutable dan irrefutable; namun, kita tetap harus familier dengan konsep refutability ini supaya kita tahu apa yang harus dilakukan pas kita ngelihat istilah ini di dalam sebuah pesan error. Di kasus-kasus seperti itu, kita perlu mengubah entah pattern-nya atau konstruk tempat kita memakai pattern tersebut, tergantung dari perilaku yang kita inginkan buat kodenya.

Mari kita lihat sebuah contoh tentang apa yang terjadi pas kita nyoba memakai sebuah pattern yang refutable di tempat di mana Rust mewajibkan pattern yang irrefutable, dan sebaliknya. Listing 19-8 menunjukkan sebuah statement let, tapi buat pattern-nya, kita menentukan Some(x), yang mana adalah sebuah pattern yang refutable. Seperti yang mungkin kita tebak, kode ini tidak bakal bisa di-compile.

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value;
}
Listing 19-8: Mencoba memakai sebuah pattern refutable bersama let

Kalau some_option_value kebetulan bernilai None, maka dia bakal gagal buat cocok sama pattern Some(x), yang artinya pattern tersebut adalah refutable. Namun, statement let cuma bisa nerima pattern yang irrefutable karena tidak ada aksi valid yang bisa dilakuin sama kodenya dengan sebuah nilai None. Saat compile time, Rust bakal komplain kalau kita udah nyoba memakai sebuah pattern refutable di tempat di mana sebuah pattern irrefutable diwajibkan:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0005]: refutable pattern in local binding
 --> src/main.rs:3:9
  |
3 |     let Some(x) = some_option_value;
  |         ^^^^^^^ pattern `None` not covered
  |
  = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
  = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
  = note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
  |
3 |     let Some(x) = some_option_value else { todo!() };
  |                                     ++++++++++++++++

For more information about this error, try `rustc --explain E0005`.
error: could not compile `patterns` (bin "patterns") due to 1 previous error

Karena kita tidak mencakup (dan memang tidak bisa mencakup!) setiap kemungkinan nilai yang valid dengan pattern Some(x), Rust dengan tepat menghasilkan error compiler.

Kalau kita punya sebuah pattern refutable di tempat di mana pattern irrefutable dibutuhkan, kita bisa memperbaikinya dengan mengubah kode yang memakai pattern tersebut: ketimbang memakai let, kita bisa memakai let else. Dengan begitu, kalau pattern-nya tidak cocok, kodenya bakal sekadar melewati (skip) kode yang ada di dalam kurung kurawal, ngasih jalan (way out) buat dia lanjut dengan cara yang valid. Listing 19-9 menunjukkan cara memperbaiki kode di Listing 19-8.

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value else {
        return;
    };
}
Listing 19-9: Memakai let...else dan sebuah blok dengan patterns refutable bukannya let

Kita udah ngasih jalan keluar buat kodenya! Kode ini benar-benar valid sekarang, meskipun ini berarti kita tidak bisa memakai sebuah pattern irrefutable tanpa menerima sebuah peringatan. Kalau kita ngasih let...else sebuah pattern yang bakal selalu cocok, kayak x, seperti yang ditunjukkan di Listing 19-10, compiler bakal ngasih sebuah peringatan.

fn main() {
    let x = 5 else {
        return;
    };
}
Listing 19-10: Mencoba memakai sebuah pattern irrefutable bersama let...else

Rust komplain kalau tidak masuk akal buat memakai let...else dengan sebuah pattern yang irrefutable:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
warning: irrefutable `let...else` pattern
 --> src/main.rs:2:5
  |
2 |     let x = 5 else {
  |     ^^^^^^^^^
  |
  = note: this pattern will always match, so the `else` clause is useless
  = help: consider removing the `else` clause
  = note: `#[warn(irrefutable_let_patterns)]` on by default

warning: `patterns` (bin "patterns") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/patterns`

Atas alasan ini, match arms wajib memakai patterns yang refutable, kecuali buat arm yang paling terakhir, yang seharusnya mencocokkan sisa nilai apa pun yang belum ditangani dengan sebuah pattern yang irrefutable. Rust mengizinkan kita buat memakai sebuah pattern irrefutable di dalam sebuah match yang cuma punya satu arm, tapi sintaks ini tidak terlalu berguna dan bisa aja diganti pakai sebuah statement let yang lebih sederhana.

Sekarang karena kita udah tahu di mana aja bisa memakai patterns dan perbedaan antara patterns refutable dan irrefutable, mari kita bahas semua sintaks yang bisa kita pakai buat bikin patterns.