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 — Pattern อาจ Fail ในการ Match ไหม

Pattern มีสองรูปแบบ — refutable และ irrefutable Pattern ที่จะ match สำหรับค่าที่เป็นไปได้ใดที่ส่งคือ irrefutable ตัวอย่างจะเป็น x ใน statement let x = 5; เพราะ x match อะไรก็ได้และดังนั้นไม่ fail ใน การ match Pattern ที่ fail ในการ match สำหรับค่าที่เป็นไปได้บางอย่างคือ refutable ตัวอย่างจะเป็น Some(x) ใน expression if let Some(x) = a_value เพราะถ้าค่าในตัวแปร a_value คือ None แทนที่จะเป็น Some pattern Some(x) จะไม่ match

Parameter ฟังก์ชัน, statement let และ loop for รับเพียง pattern irrefutable เพราะโปรแกรมไม่สามารถทำอะไรที่มีความหมายเมื่อค่าไม่ match Expression if let และ while let และ statement let...else รับทั้ง pattern refutable และ irrefutable แต่ compiler เตือนต่อ pattern irrefutable เพราะตามนิยาม พวกมันตั้งใจจัดการ failure ที่เป็นไปได้ — functionality ของ conditional คือในความสามารถของมันที่จะทำต่างขึ้นกับ success หรือ failure

โดยทั่วไป คุณไม่ต้องกังวลเกี่ยวกับความแตกต่างระหว่าง pattern refutable และ irrefutable; อย่างไรก็ตาม คุณต้องคุ้นเคยกับแนวคิดของ refutability เพื่อให้คุณตอบสนองได้เมื่อคุณเห็นมันในข้อความ error ในกรณีเหล่านั้น คุณจะต้องเปลี่ยนทั้ง pattern หรือ construct ที่คุณใช้ pattern กับ ขึ้นกับพฤติกรรมที่ตั้งใจของโค้ด

มาดูตัวอย่างของสิ่งที่เกิดขึ้นเมื่อเราพยายามใช้ pattern refutable ที่ Rust ต้องการ pattern irrefutable และในทางกลับกัน Listing 19-8 แสดง statement let แต่สำหรับ pattern เราระบุ Some(x), pattern refutable ดังที่คุณคาดเดา โค้ดนี้จะไม่ compile

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value;
}
Listing 19-8: พยายามใช้ pattern refutable กับ let

ถ้า some_option_value เป็นค่า None มันจะ fail ในการ match pattern Some(x) หมายความว่า pattern เป็น refutable อย่างไรก็ตาม statement let รับเพียง pattern irrefutable ได้เพราะไม่มีอะไร valid ที่โค้ดทำกับ ค่า None ได้ ที่ compile time Rust จะบ่นว่าเราพยายามใช้ pattern refutable ที่ pattern irrefutable ถูกต้องการ:

$ 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

เพราะเราไม่ครอบคลุม (และไม่สามารถครอบคลุม!) ทุกค่า valid กับ pattern Some(x) Rust ผลิต error compiler อย่างถูกต้อง

ถ้าเรามี pattern refutable ที่ pattern irrefutable ถูกต้องการ เรา fix มันโดยเปลี่ยนโค้ดที่ใช้ pattern ได้ — แทนที่จะใช้ let เราใช้ let...else ได้ แล้ว ถ้า pattern ไม่ match โค้ดใน curly bracket จะ จัดการค่า Listing 19-9 แสดงวิธี fix โค้ดใน Listing 19-8

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value else {
        return;
    };
}
Listing 19-9: ใช้ let...else และ block กับ pattern refutable แทน let

เราให้โค้ดทางออก! โค้ดนี้ valid อย่างสมบูรณ์ แม้มันหมายความว่าเราไม่ สามารถใช้ pattern irrefutable โดยไม่รับ warning ถ้าเราให้ let...else pattern ที่จะ match เสมอ เช่น x ดังที่แสดงใน Listing 19-10 compiler จะให้ warning

fn main() {
    let x = 5 else {
        return;
    };
}
Listing 19-10: พยายามใช้ pattern irrefutable กับ let...else

Rust บ่นว่ามันไม่สมเหตุสมผลที่จะใช้ let...else กับ pattern 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`

เพราะเหตุผลนี้ match arm ต้องใช้ pattern refutable ยกเว้น arm สุดท้าย ซึ่งควร match ค่าที่เหลือใดกับ pattern irrefutable Rust อนุญาตให้เรา ใช้ pattern irrefutable ใน match กับเพียงหนึ่ง arm ได้ แต่ syntax นี้ ไม่มีประโยชน์เป็นพิเศษและถูกแทนที่ด้วย statement let ที่ง่ายกว่าได้

ตอนนี้คุณรู้ที่จะใช้ pattern และความแตกต่างระหว่าง pattern refutable และ irrefutable มาครอบคลุม syntax ทั้งหมดที่เราใช้สร้าง pattern ได้