مقدمه
در دو درس گذشته، با مکانهای استفاده از الگوها و تفاوت بین الگوهای ردپذیر و ردناپذیر آشنا شدیم.
در این درس، به بررسی تمام سینتکسهای مختلفی که میتوانید در یک الگو به کار ببرید، میپردازیم.
این سینتکسها به ما اجازه میدهند تا ساختارهای بسیار دقیق و پیچیدهای را برای تطبیق با دادههای
خود تعریف کنیم.
سینتکسهای مختلف الگوها
- تطبیق با لیترالها (Matching Literals): شما میتوانید مستقیماً با یک مقدار لیترال
مطابقت دهید. match x { 1 => ..., _ => ... }
- تطبیق با متغیرهای نامگذاری شده (Matching Named Variables): یک متغیر نامگذاری شده در
یک الگو، یک الگوی ردناپذیر است که با هر مقداری مطابقت پیدا کرده و آن مقدار را به متغیر
bind میکند.
- چندین الگو (Multiple Patterns): شما میتوانید با استفاده از عملگر | (OR)،
چندین
الگو را در یک شاخه match ترکیب کنید. match x { 1 | 2 => ..., _
=> ... }
- تطبیق با بازهها (Matching Ranges): با استفاده از ..=
میتوانید یک بازه از مقادیر
را مطابقت دهید (این سینتکس فقط برای مقادیر عددی و char معتبر است). match x { 1..=5 =>
..., _ => ... }
- Destructuring: شما میتوانید الگوها را برای از هم باز کردن (destructure) ساختار
struct، enum، تاپل و اسلایسها استفاده کنید.
src/main.rs
struct Point { x: i32, y: i32 }
fn main() {
let p = Point { x: 0, y: 7 };
let Point { x, y } = p;
assert_eq!(0, x);
match p {
Point { x, y: 0 } => println!("On the x axis at {}", x),
Point { x: 0, y } => println!("On the y axis at {}", y),
Point { x, y } => println!("On neither axis: ({}, {})", x, y),
}
}
نادیده گرفتن مقادیر در یک الگو
گاهی اوقات شما به تمام مقادیر یک الگو نیاز ندارید. Rust به شما اجازه میدهد تا بخشهایی از یک
الگو را با استفاده از _ یا .. نادیده بگیرید.
- `_`: یک مقدار واحد را نادیده میگیرد و به هیچ متغیری bind نمیشود.
- `..`: چندین مقدار را در یک ساختار مانند struct یا enum نادیده میگیرد.
src/main.rs
fn foo(_: i32, y: i32) {
println!("This code only uses the y parameter: {}", y);
}
struct Point { x: i32, y: i32, z: i32 }
let origin = Point { x: 0, y: 0, z: 0 };
match origin {
Point { x, .. } => println!("x is {}", x),
}
الگوهای پیشرفته
در این بخش، با چند قابلیت پیشرفتهتر الگوها در Rust آشنا میشویم. این قابلیتها به شما اجازه
میدهند تا تطبیقهای پیچیدهتر و دقیقتری را روی دادهها انجام دهید. از جمله این امکانات میتوان
به match guards (شرطهای اضافی روی الگوها) و @ bindings (اتصال مقدار به متغیر
هنگام تطبیق با یک الگو) اشاره کرد.
Match Guards
یک «نگهبان مچ» یا match guard یک شرط if اضافی است که بعد از یک الگو در یک شاخه
match
قرار میگیرد. این شاخه تنها در صورتی اجرا میشود که هم الگو مطابقت داشته باشد و هم شرط if
برابر با true باشد.
src/main.rs
let num = Some(4);
match num {
Some(x) if x % 2 == 0 => println!("The number {} is even", x),
Some(x) => println!("The number {} is odd", x),
None => (),
}
@ Bindings
عملگر @ به شما اجازه میدهد تا همزمان هم بررسی کنید که یک مقدار با یک الگو مطابقت دارد و
هم آن
مقدار را به یک متغیر جدید bind کنید.
src/main.rs
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id: id_variable @ 3..=7,
} => println!("Found an id in range: {}", id_variable),
Message::Hello { id: 10..=12 } => {
println!("Found an id in another range")
}
Message::Hello { id } => println!("Found some other id: {}", id),
}
در اینجا، ما بررسی میکنیم که آیا فیلد id در بازه ۳ تا ۷ قرار دارد یا خیر. اگر اینطور
باشد، مقدار آن را به متغیر id_variable نیز bind میکنیم تا بتوانیم از آن در بدنه
شاخه
استفاده کنیم.
در این درس، تمام سینتکسهای مختلفی را که Rust برای pattern matching ارائه میدهد، مرور
کردیم.
این الگوها ابزارهای بسیار قدرتمند و گویایی هستند که به ما اجازه میدهند تا منطقهای پیچیدهای را
بر اساس ساختار دادهها پیادهسازی کنیم. با این درس، فصل «بررسی دقیقتر Pattern Matching» به
پایان میرسد. در فصل بعدی، به سراغ «قابلیتهای پیشرفتهتر Rust» خواهیم رفت و با مفاهیمی مانند کد
ناامن، traitهای پیشرفته و ماکروها آشنا خواهیم شد.