مقدمه

الگوها در Rust به دو دسته تقسیم می‌شوند: «ردپذیر» (refutable) و «ردناپذیر» (irrefutable). درک این تفاوت برای استفاده صحیح از الگوها در زمینه‌های مختلف ضروری است.

  • الگوهای ردناپذیر (Irrefutable): الگویی است که برای هر مقدار ممکنی که به آن داده شود، مطابقت پیدا می‌کند. برای مثال، الگوی x در دستور let x = 5; یک الگوی ردناپذیر است، زیرا x با هر مقداری مطابقت پیدا کرده و به آن bind می‌شود.
  • الگوهای ردپذیر (Refutable): الگویی است که ممکن است برای یک مقدار خاص، مطابقت پیدا نکند. برای مثال، الگوی Some(x) در عبارت if let Some(x) = a_value یک الگوی ردپذیر است، زیرا اگر مقدار a_value برابر با None باشد، الگو مطابقت پیدا نخواهد کرد.

زمینه استفاده از هر الگو

زبان Rust به ما اجازه نمی‌دهد که از هر الگویی در هر جایی استفاده کنیم. این محدودیت به کامپایلر اجازه می‌دهد تا در زمان کامپایل، خطاهای منطقی را تشخیص دهد.

الگوهای ردناپذیر: let و حلقه‌های for

دستورات let، پارامترهای توابع و حلقه‌های for تنها می‌توانند الگوهای ردناپذیر را بپذیرند. دلیل این امر این است که این ساختارها برای مقادیر نامنطبق، هیچ راه بازگشتی یا رفتار جایگزینی ندارند. اگر یک الگو در این زمینه‌ها شکست بخورد، برنامه در یک وضعیت نامعتبر قرار می‌گیرد.

برای مثال، کد زیر کامپایل نخواهد شد:

Copy Icon src/main.rs
// This code won't compile!
// let Some(x) = some_option_value;

اگر some_option_value برابر با None باشد، الگوی Some(x) مطابقت پیدا نمی‌کند و برنامه نمی‌داند چه کاری باید انجام دهد. کامپایلر Rust با جلوگیری از کامپایل این کد، ما را از این خطای منطقی محافظت می‌کند.

الگوهای ردپذیر: match و if let

در مقابل، شاخه‌های یک عبارت match (به جز آخرین شاخه که باید تمام حالات باقی‌مانده را پوشش دهد) و دستور if let، باید از الگوهای ردپذیر استفاده کنند. این ساختارها ذاتاً برای مدیریت شرایطی طراحی شده‌اند که یک الگو ممکن است مطابقت پیدا نکند و یک رفتار جایگزین برای آن تعریف شده است.

Copy Icon src/main.rs
let some_option_value: Option<i32> = None;

// `if let` handles the case where the pattern doesn't match by doing nothing.
if let Some(x) = some_option_value {
    println!("{}", x);
}

// `match` requires all possible cases to be handled.
match some_option_value {
    Some(x) => println!("{}", x),
    None => (), // We explicitly handle the `None` case.
}

در هر دو عبارت if let و match، ما به صورت صریح حالتی را که الگو مطابقت پیدا نمی‌کند، مدیریت کرده‌ایم.

خلاصه

در این درس با تفاوت کلیدی بین الگوهای ردپذیر و ردناپذیر و اینکه هر کدام در چه زمینه‌ای از زبان Rust قابل استفاده هستند، آشنا شدیم. این تفکیک به کامپایلر اجازه می‌دهد تا در زمان کامپایل، اطمینان حاصل کند که تمام حالات ممکن توسط کد ما پوشش داده شده‌اند و از بروز خطاهای منطقی جلوگیری می‌کند.

الگوها بسیار قدرتمندتر از چیزی هستند که تاکنون دیده‌ایم. در درس بعدی، به بررسی «تمام سینتکس‌های الگو» خواهیم پرداخت و با قابلیت‌های پیشرفته‌تری مانند تطبیق روی لیترال‌ها، بازه‌ها، و استفاده از match guardها آشنا خواهیم شد.