Rust: المطابقة المتقدمة للأنماط في لغة Rust
آخر تحديث: 2026-08-26
تُعد مطابقة الأنماط إحدى أقوى ميزات لغة Rust، وتتيح لك التقنيات المتقدمة مثل
matchالحراس، و@الارتباطات، والأنماط المتعددة، ممارسة تحكم أكثر دقة في عمليات المطابقة — ليس فقط «مطابقة القيم»، بل «مطابقة الشروط».
إذا كانت مطابقة الأنماط الأساسية تشبه السؤال متعدد الخيارات (اختيار فرع واحد لتنفيذه)، فإن مطابقة الأنماط المتقدمة تشبه السؤال المركب — حيث يمكنك التحقق من القيم، وربط المراجع، وتقييم الشروط الإضافية، كل ذلك في الوقت نفسه.
1. ما ستتعلمه
- استخدم واقي المطابقة
ifلإضافة شروط إضافية إلى فرع المطابقة - استخدم
@لربط القيم في آن واحد عند العثور على تطابق - استخدم
|لدمج أنماط متعددة (مطابقة الأنماط المتعددة) - استخدم
..و..=لمطابقة النطاق - استخدم الماكرو
matches!لإجراء عمليات فحص مطابقة منطقية موجزة - تحليل النمط المتداخل للهياكل والتعدادات
2. قصة عن تقييم أداء الموظف
(1) التحدي: استخدام سلسلة من عبارات «if-else» للحصول على تقييم شامل
توم هو مدير الموارد البشرية في الشركة، ويحتاج إلى إعطاء تقييمات شاملة استنادًا إلى درجات الأداء وسجلات الحضور للموظفين.
القواعد هي كما يلي:
| المستوى | درجة الأداء | شروط الحضور |
|---|---|---|
| A+ | ≥95 | حضور كامل (≥22 يومًا) |
| أ | ≥85 | الحضور ≥ 20 يومًا |
| ب | ≥70 | الحضور ≥ 18 يومًا |
| C | ≥60 | — |
| D | <60 | — |
في البداية، كتب سلسلة طويلة من if-else:
fn rate_employee(score: u32, attendance: u32) -> &'static str {
if score >= 95 && attendance >= 22 {
"A+"
} else if score >= 85 && attendance >= 20 {
"A"
} else if score >= 70 && attendance >= 18 {
"B"
} else if score >= 60 {
"C"
} else {
"D"
}
}
على الرغم من أن هذا الكود يعمل، إلا أنه ينطوي على عدة مشكلات: فالشروط مبعثرة عبر سلسلة من عبارات «if-else»، مما يجعل قراءته صعبة، كما أنه لا يوجد فحص شامل — فإذا أُضيفت قواعد تقييم في المستقبل، فسيكون من السهل تجاهلها. علاوة على ذلك، فإن الارتباط بين الدرجات والحضور ليس واضحًا بما فيه الكفاية.
(2) مناهج متقدمة لمطابقة الأنماط في لغة Rust
fn rate_employee(score: u32, attendance: u32) -> &'static str {
match score {
// Guard Requirements: Meeting the Score Requirement + Attendance Requirements Met
s if s >= 95 && attendance >= 22 => "A+",
s if s >= 85 && attendance >= 20 => "A",
s if s >= 70 && attendance >= 18 => "B",
s if s >= 60 => "C",
_ => "D", // Fallback: score < 60
}
}
fn main() {
let employees = [
("Alice", 98, 23),
("Bob", 88, 21),
("Charlie", 88, 17), // High scores but insufficient attendance
("Diana", 72, 19),
("Eve", 55, 20),
];
for (name, score, attendance) in &employees {
let rating = rate_employee(*score, *attendance);
println!("{:8} | Score: {:2} | Attendance: {:2} days | Rating: {}",
name, score, attendance, rating);
}
}
الناتج:
Alice | Score: 98 | Attendance: 23 days | Rating: A+
Bob | Score: 88 | Attendance: 21 days | Rating: A
Charlie | Score: 88 | Attendance: 17 days | Rating: B
Diana | Score: 72 | Attendance: 19 days | Rating: B
Eve | Score: 55 | Attendance: 20 days | Rating: D
matchيوضح الحارس شروط كل فرع على الفور: حيث تظهر بوضوح العلاقة "AND" بين الدرجة والحضور._ويضمن الفرع الشامل استيفاء جميع الحالات. علاوة على ذلك، فإن دلالةs if s >= ...بالنسبة لنطاقات الدرجات المختلفة أكثر بديهية من دلالةelse if.
3. نظرة عامة على المطابقة المتقدمة للأنماط
(1) خريطة مفاهيمية
graph TB
A[Advanced Pattern Matching] --> B[match Guard]
A --> C[@ Bind]
A --> D[Multimode |]
A --> E[Range Matching]
A --> F[matches! Macro]
A --> G[Deconstruction Patterns]
B --> B1["match x { n if n > 10 => ... }"]
C --> C1["match x { e @ 1..=5 => ... }"]
D --> D1["match x { 1 | 3 | 5 => ... }"]
E --> E1["match x { 1..=10 => ... }"]
F --> F1["if matches!(x, 1..=5) { ... }"]
G --> G1["let Point { x, y } = p;"]
G --> G2["match opt { Some(v @ 1..=10) => ... }"]
(2) مقارنة بين طرق المطابقة
| الطريقة | الصيغة | متى تُستخدم | مثال |
|---|---|---|---|
| المطابقة الأساسية | match x { PAT => .. } |
مطابقة القيمة البسيطة | match x { 1 => "one" } |
| حارس المباراة | match x { PAT if COND => .. } |
المباراة + شروط إضافية | n if n > 10 && n % 2 == 0 |
| @ الربط | e @ PAT => .. |
قيمة الربط عند التطابق | e @ 1..=10 => e |
| أوضاع متعددة | | PAT1 | PAT2 => .. |
تتشارك الأوضاع المتعددة نفس الفرع | 1 | 3 | 5 => "odd" |
| النطاق ..= | PAT1..=PAT2 => .. |
يطابق نطاقًا من القيم | 1..=5 => "small" |
| ماكرو "matches!" | matches!(x, PAT) |
نتيجة منطقية فقط | if matches!(x, 1..=5) |
| التفكيك | let PAT = value |
تفكيك الأنواع المركبة | let (a, b) = pair |
(3) مرجع سريع لأنواع الأنماط
| نوع النمط | الصيغة | الهدف المطابق | مثال |
|---|---|---|---|
| النص الحرفي | 1 / "hello" |
القيمة الدقيقة | match x { 1 => ... } |
| ربط المتغير | x |
أي قيمة، مرتبطة بـ x | match x { n => ... } |
| حرف البدل | _ |
أي قيمة، يتم تجاهلها | match x { _ => ... } |
| أوضاع متعددة | 1 | 2 | 3 |
قيم متعددة | match x { 1 | 2 => ... } |
| النطاق | 1..=5 |
نطاق الفاصل المغلق | match x { 1..=5 => ... } |
| تحليل المجموعات | (a, b) |
المجموعات | match pair { (x, y) => ... } |
| تفكيك الهياكل | Point { x, y } |
الهياكل | match p { Point { x, y } => ... } |
| تحليل عمليات التعداد | Some(v) |
أشكال التعداد المختلفة | match opt { Some(v) => ... } |
| @ الربط | e @ 1..=10 |
النطاق + الربط | match x { e @ 1..=10 => ... } |
| الحراسة | x if x > 0 |
الوضع + الشروط | match x { n if n > 0 => ... } |
| تجاهل الباقي | .. |
تجاهل بعض الحقول | match p { Point { x, .. } => ... } |
4. أمثلة على المطابقة المتقدمة
(1) ▶ المثال:Match Guard + @ Binding + Multi-Mode (مستوى الصعوبة ⭐⭐)
// ============================================
// Comprehensive Example: Employee Performance Rating System
// Display match Guard, @ Bind, Multi-pattern
// ============================================
#[derive(Debug)]
enum Department {
Engineering,
Sales,
HR,
Management,
}
#[derive(Debug)]
struct Employee {
name: String,
score: u32,
attendance: u32,
department: Department,
}
impl Employee {
/// Rate employee using advanced pattern matching.
/// Returns (rating, bonus_percentage).
fn rate(&self) -> (&'static str, u32) {
match (self.score, self.attendance) {
// @ binding: capture the matched score value
s @ 95..=100 if self.attendance >= 22 => {
println!(" [@ binding] score {} captured for A+ rating", s);
("A+", 30)
}
s @ 85..=94 if self.attendance >= 20 => {
println!(" [@ binding] score {} captured for A rating", s);
("A", 20)
}
s @ 70..=84 if self.attendance >= 18 => {
println!(" [@ binding] score {} captured for B rating", s);
("B", 10)
}
// Multi-pattern: 60..=69 OR exactly 70 with low attendance
60..=69 | 70..=84 => {
("C", 5) // No @ binding needed here
}
// match guard with combined conditions
s if s < 60 => {
println!(" [guard] score {} is below 60, rating D", s);
("D", 0)
}
// Catch-all: should not reach normally
_ => {
println!(" [wildcard] unexpected combination");
("Unknown", 0)
}
}
}
/// Check if this employee qualifies for special bonus
/// using matches! macro.
fn has_special_bonus(&self) -> bool {
// matches! returns true if the pattern matches
matches!(self.department, Department::Engineering | Department::Management)
&& self.score >= 90
}
}
fn main() {
let employees = vec![
Employee {
name: String::from("Alice"),
score: 97,
attendance: 23,
department: Department::Engineering,
},
Employee {
name: String::from("Bob"),
score: 88,
attendance: 21,
department: Department::Sales,
},
Employee {
name: String::from("Charlie"),
score: 88,
attendance: 17,
department: Department::HR,
},
Employee {
name: String::from("Diana"),
score: 65,
attendance: 20,
department: Department::HR,
},
Employee {
name: String::from("Eve"),
score: 42,
attendance: 15,
department: Department::Management,
},
];
for emp in &employees {
println!("{}:", emp.name);
let (rating, bonus) = emp.rate();
let special = emp.has_special_bonus();
println!(" Rating: {}, Bonus: {}%, Special: {}",
rating, bonus, special);
println!();
}
}
الناتج:
Alice:
[@ binding] score 97 captured for A+ rating
Rating: A+, Bonus: 30%, Special: true
Bob:
[@ binding] score 88 captured for A rating
Rating: A, Bonus: 20%, Special: false
Charlie:
[@ binding] score 88 captured for B rating
Rating: B, Bonus: 10%, Special: false
Diana:
Rating: C, Bonus: 5%, Special: false
Eve:
[guard] score 42 is below 60, rating D
Rating: D, Bonus: 0%, Special: true
يوضح هذا المثال بشكل شامل ثلاث تقنيات مطابقة متقدمة:
@تربط القيم الملتقطة لاستخدامها لاحقًا؛ifتستخدم الحراس لإضافة شروط إضافية (مثل متطلبات الحضور)؛|تستخدم الوضع المتعدد للسماح لنطاقات مختلفة بمشاركة نفس الفرع؛ وmatches!تستخدم ماكروًّا لتحديد «ما إذا كان ينتمي إلى أحد المتغيرات العديدة» بشكل موجز.
(2) ▶ المثال:تفكيك الهياكل المتداخلة وقوائم التعداد (مستوى الصعوبة ⭐⭐⭐)
// ============================================
// Nested Deconstruction: Matching Enums in Structures, Tuples in Enumerations
// ============================================
#[derive(Debug)]
enum TaskStatus {
Pending,
InProgress,
Completed { finished_at: String, reviewer: String },
Blocked { reason: String, blocked_by: String },
}
#[derive(Debug)]
struct Task {
id: u32,
title: String,
status: TaskStatus,
priority: u8, // 1 (highest) to 5 (lowest)
}
fn analyze_task(task: &Task) {
// Destructure the Task struct directly in match
match task {
// Multi-pattern: priority 1 or 2 with any status
Task { priority: 1..=2, .. } => {
println!(" [range] High priority task (level {})", task.priority);
}
// Destructure both Task and TaskStatus::Completed
Task {
id,
title,
status:
TaskStatus::Completed {
finished_at,
reviewer,
},
priority,
} => {
println!(" [nested destructure] Task #{} '{}' completed by {} at {} (priority {})",
id, title, reviewer, finished_at, priority);
}
// Destructure TaskStatus::Blocked with @ binding on reason
Task {
status:
TaskStatus::Blocked {
reason,
blocked_by,
},
..
} => {
println!(" [@ binding] Task '{}' blocked by {}: {}",
task.title, blocked_by, reason);
}
// Match guard on enum variant
Task {
status: TaskStatus::InProgress,
priority,
..
} if *priority <= 3 => {
println!(" [guard] Important in-progress task (priority {})", priority);
}
// Wildcard for remaining
_ => {
println!(" [wildcard] Task '{}': status {:?}", task.title, task.status);
}
}
}
fn main() {
let tasks = vec![
Task {
id: 1,
title: String::from("Security audit"),
status: TaskStatus::Completed {
finished_at: String::from("2026-07-01"),
reviewer: String::from("Alice"),
},
priority: 1,
},
Task {
id: 2,
title: String::from("Update dependencies"),
status: TaskStatus::Blocked {
reason: String::from("Waiting for approval"),
blocked_by: String::from("Manager"),
},
priority: 2,
},
Task {
id: 3,
title: String::from("Write documentation"),
status: TaskStatus::InProgress,
priority: 3,
},
Task {
id: 4,
title: String::from("Fix typo in README"),
status: TaskStatus::Pending,
priority: 5,
},
];
for task in &tasks {
println!("Task #{}: {}", task.id, task.title);
analyze_task(task);
println!();
}
}
الناتج:
Task #1: Security audit
[nested destructure] Task #1 'Security audit' completed by Alice at 2026-07-01 (priority 1)
Task #2: Update dependencies
[range] High priority task (level 2)
Task #3: Write documentation
[guard] Important in-progress task (priority 3)
Task #4: Fix typo in README
[wildcard] Task 'Fix typo in README': status Pending
يُعد التفكيك المتداخل إحدى الميزات القوية لمطابقة الأنماط في لغة Rust: حيث يمكنك تفكيك الهياكل (structs) والقوائم (enums) والمجموعات (tuples) في آن واحد ضمن فرع
matchواحد. يتجاهل..الحقول التي لا تهمك، بينما يقوم@بربط القيم الداخلية والتقاطها. لاحظ أنTask { priority: 1..=2, .. }يطابق المهام ذات أي أولوية تساوي 1 أو 2 — لأنه على الرغم من أنBlockedيأتي لاحقًا في النمط، فإنrangeيطابق المهام ذات الأولوية = 2 أولاً.
(3) ▶ المثال:دمج الماكرو matches! مع if let (مستوى الصعوبة: ⭐⭐)
// ============================================
// matches! Macro: Simple Boolean Pattern Matching
// ============================================
#[derive(Debug, PartialEq)]
enum HttpStatus {
Ok,
NotFound,
ServerError(u16),
Redirect(u16),
}
/// Check if a status is a success (2xx).
fn is_success(status: &HttpStatus) -> bool {
matches!(status, HttpStatus::Ok)
}
/// Check if a status is a server error (5xx).
fn is_server_error(status: &HttpStatus) -> bool {
matches!(status, HttpStatus::ServerError(_))
}
/// Check if a status is a redirect (3xx) with specific code.
fn is_redirect_to(status: &HttpStatus, code: u16) -> bool {
matches!(status, HttpStatus::Redirect(c) if *c == code)
}
/// Get status category using multiple matches! checks.
fn categorize(status: &HttpStatus) -> &'static str {
if matches!(status, HttpStatus::Ok) {
"Success"
} else if matches!(status, HttpStatus::Redirect(301 | 302)) {
"Temporary Redirect"
} else if matches!(status, HttpStatus::Redirect(_)) {
"Other Redirect"
} else if matches!(status, HttpStatus::NotFound) {
"Not Found (404)"
} else if matches!(status, HttpStatus::ServerError(500..=599)) {
"Server Error"
} else {
"Unknown"
}
}
fn main() {
let statuses = vec![
HttpStatus::Ok,
HttpStatus::NotFound,
HttpStatus::Redirect(301),
HttpStatus::Redirect(307),
HttpStatus::ServerError(500),
HttpStatus::ServerError(503),
];
for status in &statuses {
println!("{:?}:", status);
println!(" is_success: {}", is_success(status));
println!(" is_server_error: {}", is_server_error(status));
println!(" is_redirect_to_301: {}", is_redirect_to(status, 301));
println!(" category: {}", categorize(status));
// if let with matches!-style pattern
if let HttpStatus::ServerError(code) = status {
println!(" >> Server error code: {}", code);
}
println!();
}
}
الناتج:
Ok:
is_success: true
is_server_error: false
is_redirect_to_301: false
category: Success
NotFound:
is_success: false
is_server_error: false
is_redirect_to_301: false
category: Not Found (404)
Redirect(301):
is_success: false
is_server_error: false
is_redirect_to_301: true
category: Temporary Redirect
Redirect(307):
is_success: false
is_server_error: false
is_redirect_to_301: false
category: Other Redirect
ServerError(500):
is_success: false
is_server_error: true
is_redirect_to_301: false
category: Server Error
ServerError(503):
is_success: false
is_server_error: true
is_redirect_to_301: false
category: Server Error
يُرجع الماكرو
matches!قيمة من نوعbool، وهي مناسبة لمطابقة الأنماط في شروطif. وهو يدعم جميع صيغ الأنماط، بما في ذلك شروطif، والأنماط المتعددة، ومطابقة النطاقات. على عكسif let، لا يقومmatches!بربط المتغيرات؛ بل يرجع فقطtrue/false— وهو الخيار الأكثر إيجازًا عندما تحتاج فقط إلى تقييم شرط دون استرداد قيمة.
(4) ▶ المثال:تمرين شامل — مُقيِّم التعبيرات (مستوى الصعوبة ⭐⭐⭐)
// ============================================
// Comprehensive Example: Implementing Expression Evaluation Using Nested Pattern Matching
// ============================================
#[derive(Debug, Clone)]
enum Expr {
Number(f64),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
Neg(Box<Expr>),
}
fn eval(expr: &Expr) -> Option<f64> {
match expr {
Expr::Number(n) => Some(*n),
Expr::Add(a, b) => Some(eval(a)? + eval(b)?),
Expr::Sub(a, b) => Some(eval(a)? - eval(b)?),
Expr::Mul(a, b) => Some(eval(a)? * eval(b)?),
Expr::Div(a, b) => {
let divisor = eval(b)?;
if divisor == 0.0 { None } else { Some(eval(a)? / divisor) }
}
Expr::Neg(a) => Some(-eval(a)?),
}
}
fn expr_to_string(expr: &Expr) -> String {
match expr {
Expr::Number(n) => format!("{:.0}", n),
Expr::Add(a, b) => format!("({} + {})", expr_to_string(a), expr_to_string(b)),
Expr::Sub(a, b) => format!("({} - {})", expr_to_string(a), expr_to_string(b)),
Expr::Mul(a, b) => format!("({} * {})", expr_to_string(a), expr_to_string(b)),
Expr::Div(a, b) => format!("({} / {})", expr_to_string(a), expr_to_string(b)),
Expr::Neg(a) => format!("-{}", expr_to_string(a)),
}
}
fn main() {
let expr1 = Expr::Add(
Box::new(Expr::Number(10.0)),
Box::new(Expr::Mul(Box::new(Expr::Number(3.0)), Box::new(Expr::Number(4.0)))),
);
let expr2 = Expr::Div(
Box::new(Expr::Sub(Box::new(Expr::Number(20.0)), Box::new(Expr::Number(5.0)))),
Box::new(Expr::Number(3.0)),
);
let expr3 = Expr::Neg(Box::new(Expr::Number(42.0)));
let expr4 = Expr::Div(Box::new(Expr::Number(10.0)), Box::new(Expr::Number(0.0)));
let exprs = [expr1, expr2, expr3, expr4];
for (i, expr) in exprs.iter().enumerate() {
let s = expr_to_string(expr);
match eval(expr) {
Some(val) => println!("Expression{}: {} = {:.2}", i + 1, s, val),
None => println!("Expression{}: {} = Error (Division by Zero)", i + 1, s),
}
}
}
الناتج:
Expression1: (10 + (3 * 4)) = 22.00
Expression2: ((20 - 5) / 3) = 5.00
Expression3: -42 = -42.00
Expression4: (10 / 0) = Error (Division by Zero)
يُعد التعداد التكراري المقترن بتفكيك المتغيرات المتداخلة أحد أقوى تطبيقات مطابقة الأنماط.
Box<Expr>يتيح للتعدادات الإشارة إلى نفسها (نظرًا لأنBoxهو مؤشر ذو حجم ثابت).evalيستخدم عامل?لنقل الأخطاء غير الصفرية بطريقة أنيقة.
(5) ▶ المثال:مطابقة الأنماط وهياكل البيانات الشبيهة بـ JSON (مستوى الصعوبة ⭐⭐⭐)
// ============================================
// Processing Dynamically Typed Data Using Pattern Matching
// ============================================
#[derive(Debug, Clone)]
enum Value {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<Value>),
Object(Vec<(String, Value)>),
}
fn type_name(val: &Value) -> &str {
match val {
Value::Null => "null",
Value::Bool(_) => "bool",
Value::Number(_) => "number",
Value::String(_) => "string",
Value::Array(_) => "array",
Value::Object(_) => "object",
}
}
fn deep_count(val: &Value) -> usize {
match val {
Value::Array(items) => items.iter().map(deep_count).sum::<usize>() + 1,
Value::Object(entries) => entries.iter().map(|(_, v)| deep_count(v)).sum::<usize>() + 1,
_ => 1,
}
}
fn find_key<'a>(val: &'a Value, key: &str) -> Option<&'a Value> {
match val {
Value::Object(entries) => entries.iter().find(|(k, _)| k == key).map(|(_, v)| v),
_ => None,
}
}
fn main() {
let data = Value::Object(vec![
("name".into(), Value::String("Alice".into())),
("age".into(), Value::Number(30.0)),
("active".into(), Value::Bool(true)),
("scores".into(), Value::Array(vec![
Value::Number(95.0),
Value::Number(88.0),
Value::Number(92.0),
])),
("address".into(), Value::Object(vec![
("city".into(), Value::String("Beijing".into())),
("zip".into(), Value::Number(100000.0)),
])),
]);
println!("Type: {}", type_name(&data));
println!("Number of nodes: {}", deep_count(&data));
if let Some(Value::String(name)) = find_key(&data, "name") {
println!("Name: {}", name);
}
if let Some(Value::Array(scores)) = find_key(&data, "scores") {
let avg: f64 = scores.iter()
.filter_map(|v| if let Value::Number(n) = v { Some(*n) } else { None })
.sum::<f64>() / scores.len() as f64;
println!("Average Score: {:.1}", avg);
}
if let Some(Value::Object(addr)) = find_key(&data, "address") {
if let Some(Value::String(city)) = find_key(&Value::Object(addr.clone()), "city") {
println!("City: {}", city);
}
}
}
الناتج:
Type: object
Number of nodes: 11
Name: Alice
Average Score: 91.7
City: Beijing
استخدم القوائم (enumerations) لمحاكاة البيانات ذات الأنواع الديناميكية (مثل JSON)، واستخراجها والتكرار عليها بأمان باستخدام مطابقة الأنماط.
find_keyاستخدمif letللوصول الآمن، وfilter_mapبالاقتران مع مطابقة الأنماط لتصفية القيم في المصفوفة وتحويلها.
❓ أسئلة شائعة
match (if guard) على الفرع match؟match هو شرط داخل فرع — حيث تتم مطابقة النمط أولاً، ثم يتم تقييم الشرط. وإذا عاد الشرط بقيمة false، ينتقل البرنامج إلى الفرع التالي (ولا ينتقل مباشرةً إلى الفرع الذي يليه!). ويمكن استخدام المتغيرات المرتبطة بمطابقة النمط في شرط الحراسة.e @ 1..=10 تعني «إذا كانت القيمة بين 1 و10، فقم بربط تلك القيمة بـ e». أما كتابة x مباشرةً فتطابق أي قيمة وتربطها بـ x. @ تسمح لك بتقييد النطاق مع الاستمرار في استخدام تلك القيمة... يطابق أي عدد من العناصر (ويتجاهلها)، بينما _ يطابق موضعًا واحدًا. عند تفكيك بنية (struct)، يتجاهل Struct { a, .. } جميع الحقول الأخرى؛ أما في التوبول (tuple)، فإن (x, .., z) يطابق العنصرين الأول والأخير. _ يتجاهل موضعًا واحدًا فقط. .. لا يمكن استخدامه إلا مرة واحدة في نفس النمط.matches! وif let؟matches! قيمة bool (دون ربط متغير)، بينما يقوم if let بربط متغير وتنفيذ كتلة التعليمات البرمجية. إذا كنت تحتاج فقط إلى التحقق من «ما إذا كان هناك تطابق» دون الحاجة إلى القيمة المطابقة، فاستخدم matches!. أما إذا كنت بحاجة إلى تحليل القيمة واستخدامها، فاستخدم if let أو match.Some(1) | Some(2)، ولكن لا يُقبل استخدام Some(1) | None، لأن Some تحتوي على عناصر بيانات بينما None لا تحتوي على أي عناصر.PartialOrd (i8-u128، u8-u128) ونوع char. على سبيل المثال، match c { 'a'..='z' => "lowercase", 'A'..='Z' => "uppercase", _ => "other" }. لا يتم دعم مطابقة النطاق لـ f64.📖 ملخص
- حارس المطابقة (إن وجد) يضيف فحصًا شرطيًّا بعد مطابقة النمط لتمكين التحكم الدقيق من خلال "النمط + الشرط"
- @ الربط يلتقط القيم عند مطابقة أنماط محددة لتجنب تكرار الحسابات أو إعادة المطابقة
- | متعدد الأوضاع يتيح لعدة أوضاع مشاركة نفس فرع الكود، مما يقلل من التكرار
- .. و ..= يمكن لمطابقة النطاقات أن تشمل نطاقات الأعداد الصحيحة ونطاقات الأحرف؛
..يتجاهل الحقول المتبقية أثناء عملية التفكيك - ماكرو matches! يُرجع قيمة منطقية؛ وهو مثالي لإجراء عمليات فحص موجزة لمطابقة الأنماط في عبارات if
- التفكيك المتداخل يتيح لك تفكيك مستويات متعددة من الأنواع المركبة — مثل البنيات (structs) والتعدادات (enumerations) والمجموعات (tuples) — في آن واحد ضمن جملة
match.
📝 تمارين
-
الصعوبة ⭐: اكتب دالة
fn describe_number(n: i32) -> &'static strتستخدم الدالةmatchومطابقة النطاق: تُرجع الدالة1..=10القيمة "صغير"، وتُرجع الدالة11..=100القيمة "متوسط"، وتُرجع الدالة101..=1000القيمة "كبير"، بينما تُرجع جميع القيم الأخرى "خارج النطاق". اختبر الدالة باستخدام القيم 5 و50 و500 و5000 في الدالةmain. -
الصعوبة ⭐⭐: عرّف قائمة
Temperatureتتضمن متغيرين:Celsius(f64)وFahrenheit(f64). اكتب دالةfn describe_temp(temp: &Temperature) -> &'static strتستخدم الماكروmatches!وشرط الحراسةifلتحديد: إذا كانت درجة الحرارة بالسيلزيوس أكبر من 30 أو بالفهرنهايت أكبر من 86، فقم بإرجاع "hot"؛ وإذا كانت درجة الحرارة بالسيلزيوس أقل من 0 أو بالفهرنهايت أقل من 32، فقم بإرجاع "cold"؛ وإلا، فقم بإرجاع "moderate". اختبر درجات الحرارة الأربع في الدالةmain. -
الصعوبة ⭐⭐⭐: عرّف بنية
Orderتحتوي علىid: u32وitems: Vec<String>وtotal: f64وstatus: OrderStatus(التعداد:Pending،Shipped { tracking: String }،Delivered { date: String }). قم بتنفيذ دالةfn summary(&self) -> Stringالتي تستخدم تفكيك متداخل وحراسmatchلإرجاع رسائل ملخص مختلفة بناءً على الحالة والمبلغ الإجمالي (على سبيل المثال، بالنسبة للطلبات التي تم شحنها والتي يزيد مجموع قيمتها عن 1,000، اعرض "تم شحن طلب عالي القيمة، التتبع: xxx"). في الدالةmain، أنشئ ثلاث طلبات واطبع ملخصاتها.