Refutability: Whether a Pattern Might Fail to Match
الگوها به دو شکل هستند: قابلرد (refutable) و غیرقابلرد (irrefutable). الگوهایی که برای هر مقدار ممکن مطابقت دارند غیرقابلرد هستند. بهعنوان مثال، x
در عبارت let x = 5;
، زیرا x
با هر چیزی مطابقت دارد و بنابراین نمیتواند از تطابق باز بماند. الگوهایی که ممکن است برای برخی مقادیر ممکن مطابقت نداشته باشند قابلرد هستند. بهعنوان مثال، Some(x)
در عبارت if let Some(x) = a_value
، زیرا اگر مقدار در متغیر a_value
None
باشد بهجای Some
، الگوی Some(x)
مطابقت نخواهد داشت.
پارامترهای تابع، عبارات let
، و حلقههای for
فقط میتوانند الگوهای غیرقابلرد بپذیرند، زیرا برنامه نمیتواند کاری معنادار انجام دهد وقتی مقادیر مطابقت ندارند. عبارات if let
و while let
و عبارت let
-else
الگوهای قابلرد و غیرقابلرد را میپذیرند، اما کامپایلر درباره الگوهای غیرقابلرد هشدار میدهد زیرا بهطور تعریفشده برای مدیریت شکست احتمالی طراحی شدهاند: عملکرد شرطی در توانایی آن است که بسته به موفقیت یا شکست بهطور متفاوت عمل کند.
بهطور کلی، نباید نیازی به نگرانی در مورد تمایز بین الگوهای قابلرد و غیرقابلرد داشته باشید؛ با این حال، باید با مفهوم قابلرد بودن آشنا باشید تا بتوانید زمانی که آن را در یک پیام خطا میبینید، واکنش نشان دهید. در این موارد، باید یا الگو را تغییر دهید یا ساختاری که الگو را با آن استفاده میکنید، بسته به رفتار موردنظر کد تغییر دهید.
بیایید به مثالی نگاه کنیم که وقتی سعی میکنیم از یک الگوی قابلرد جایی که راست نیاز به یک الگوی غیرقابلرد دارد استفاده کنیم، و برعکس، چه اتفاقی میافتد. فهرست 19-8 یک عبارت let
را نشان میدهد، اما برای الگو ما Some(x)
، یک الگوی قابلرد مشخص کردهایم. همانطور که ممکن است انتظار داشته باشید، این کد کامپایل نخواهد شد.
fn main() {
let some_option_value: Option<i32> = None;
let Some(x) = some_option_value;
}
let
اگر مقدار some_option_value
None
باشد، مطابقت با الگوی Some(x)
شکست خواهد خورد، به این معنا که الگو قابلرد است. با این حال، عبارت let
فقط میتواند یک الگوی غیرقابلرد بپذیرد زیرا چیزی معتبر وجود ندارد که کد بتواند با مقدار None
انجام دهد. در زمان کامپایل، راست شکایت میکند که ما سعی کردهایم از یک الگوی قابلرد جایی که یک الگوی غیرقابلرد نیاز است استفاده کنیم:
$ 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/ch18-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
زیرا ما هر مقدار معتبری را با الگوی Some(x)
پوشش ندادیم (و نمیتوانستیم پوشش دهیم!)، راست بهدرستی یک خطای کامپایلر تولید میکند.
اگر یک الگوی قابلرد داشته باشیم جایی که یک الگوی غیرقابلرد نیاز است، میتوانیم با تغییر کدی که از الگو استفاده میکند آن را رفع کنیم: بهجای استفاده از let
، میتوانیم از if let
استفاده کنیم. سپس اگر الگو مطابقت نداشته باشد، کد بهسادگی از اجرای کد داخل آکولادها صرفنظر میکند و راهی برای ادامه معتبر فراهم میکند. فهرست 19-9 نشان میدهد که چگونه کد در فهرست 19-8 را رفع کنیم.
fn main() { let some_option_value: Option<i32> = None; if let Some(x) = some_option_value { println!("{x}"); } }
if let
و یک بلوک با الگوهای قابلرد بهجای let
ما به کد یک مسیر خروجی دادیم! این کد اکنون کاملاً معتبر است. با این حال، اگر به if let
یک الگوی غیرقابلرد (الگویی که همیشه مطابقت دارد)، مانند x
، بدهیم، همانطور که در فهرست 19-10 نشان داده شده است، کامپایلر یک هشدار خواهد داد.
fn main() { if let x = 5 { println!("{x}"); }; }
if let
راست شکایت میکند که استفاده از if let
با یک الگوی غیرقابلرد منطقی نیست:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
warning: irrefutable `if let` pattern
--> src/main.rs:2:8
|
2 | if let x = 5 {
| ^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
= 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`
5
به این دلیل، بازوهای match باید از الگوهای قابلرد استفاده کنند، بهجز بازوی آخر که باید با یک الگوی غیرقابلرد هر مقدار باقیمانده را مطابقت دهد. راست به ما اجازه میدهد از یک الگوی غیرقابلرد در یک match
با تنها یک بازو استفاده کنیم، اما این نحو بهویژه مفید نیست و میتواند با یک عبارت سادهتر let
جایگزین شود.
اکنون که میدانید کجا میتوان از الگوها استفاده کرد و تفاوت بین الگوهای قابلرد و غیرقابلرد چیست، بیایید تمام نحوهایی که میتوانیم برای ایجاد الگوها استفاده کنیم را بررسی کنیم.