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;
}
letKalau 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;
};
}
let...else dan sebuah blok dengan patterns refutable bukannya letKita 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;
};
}
let...elseRust 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.