Rust: التحكم في تدفق Rust

آخر تحديث: 2026-08-26

match في لغة Rust ليست switch — فهي أقوى لأن المُجمِّع يتحقق للتأكد من أنك قد غطيت جميع الحالات.

يُعد تدفق التحكم العمود الفقري للبرنامج. وبالإضافة إلى if/else التقليدية، تتمتع لغة Rust بميزتين رئيسيتين هما: match مطابقة الأنماط وif let السكر النحوي.


1. ما ستتعلمه



2. قصة حكم لعبة «الحجر والورق والمقص»

(1) التحدي: كتابة منطق اتخاذ القرار في لعبة «الحجر والورق والمقص» باستخدام عبارات if-else

إيما هي مطورة ألعاب ترغب في إضافة ميزة حجر، ورق، مقص إلى لعبتها:

TEXT 📖 للعرض فقط
Player's turn: Rock (1), Scissors (2), Paper (3)
Computer: Pick one at random
Needs to be determined: Win, Lose, or Tie

الرمز الذي يستخدم if/else للتحديد:

TEXT 📖 للعرض فقط
if Player == 1 && Computer == 1 → Tie
else if Player == 1 && Computer == 2 → Win
else if Player == 1 && Computer == 3 → Lose
... Still needs 6 branches!

عند كتابة عدد كبير من أحرف if/else، من السهل أن تفوتك بعض التوليفات، كما يصعب قراءتها.

(2) حل أنيق لعبارة match في لغة Rust

RUST
fn main() {
    let player = 1;    // Rock
    let computer = 2;  // Scissors

    let result = match (player, computer) {
        (1, 1) | (2, 2) | (3, 3) => "Tie",
        (1, 2) | (2, 3) | (3, 1) => "You Win!",
        (1, 3) | (2, 1) | (3, 2) => "You Lose!",
        _ => "Invalid Punch",
    };

    println!("Results: {}", result);
}

match الأمر أشبه بـالحكم هنا: فهو يربط بين مجموعات اللكمات التي يقوم بها اللاعب وتلك التي يقوم بها الكمبيوتر بنسبة واحد إلى واحد، دون أي إغفال أو غموض. ويقوم المُجمِّع بالتحقق مما إذا كنت قد كتبت جميع المجموعات التسع أم لا.



3. التفرع الشرطي باستخدام if/else

(1) الاستخدام الأساسي

RUST
fn main() {
    let number = 7;

    if number < 5 {
        println!("A number less than 5");
    } else if number == 5 {
        println!("The number equals 5");
    } else {
        println!("The number is greater than 5");
    }
}

(2) إذا كان if تعبيرًا — فيمكنه إرجاع قيمة

RUST
let condition = true;
let value = if condition { 5 } else { 6 };
// Note:The two branches must be of the same type.
println!("value = {}", value);  // value = 5

هام: يجب أن يكون فرعا if من نفس النوع. سيؤدي استخدام if true { 5 } else { "six" } إلى حدوث خطأ في الترجمة.



4. مطابقة الأنماط

(1) قواعد النحو الأساسية

100%
graph TB
    A[match Expressions] --> B[Each branch = A Pattern]
    B --> C[Pattern match successful → Run this branch]
    B --> D[Pattern Mismatch → Try the next branch]
    A --> E[Finally, there must be _ Wildcard Fallback]
RUST
let number = 3;
match number {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    _ => println!("other"),  // Wildcard——Match all remaining cases
}

(2) القواعد الأساسية لـ "match"

القاعدة الوصف
الشمولية يجب أن تغطي جميع القيم الممكنة؛ وسيؤدي إغفال أي فرع إلى حدوث خطأ في الترجمة
حرف البدل _ يطابق جميع الحالات المتبقية؛ يُوضع في النهاية
متعدد الأوضاع | فرع واحد يتطابق مع أنماط متعددة: 1 | 2 =>
مطابقة النطاق 1..=5 يطابق الفاصل المغلق من 1 إلى 5
حارس شروط إضافية: x if x > 5 =>

(3) مقارنة بين if وmatch

الخصائص if/else match
طريقة المطابقة الشرط المنطقي مطابقة النمط
فحص شامل لا شيء (يمكن حذف الباقي) إلزامي (يجب أن يشمل جميع القيم)
قدرات التفكيك لا شيء يدعم تفكيك قوائم التعداد، والمجموعات، والهياكل
المطابقة متعددة القيم تتطلب التسلسل `
حالات الاستخدام فحوصات النطاق، المنطق البولياني معالجة التعداد، تصنيف القيم، التفكيك


5. if let — «السكر النحوي»

عندما يكون اهتمامك منصبًّا على نمط معين فقط دون القيم الأخرى، فإن if let تكون أكثر إيجازًا من match:

RUST
let optional = Some(5);

// match Writing Style (verbose)
match optional {
    Some(value) => println!("The value is: {}", value),
    _ => (),  // You must write this empty branch.
}

// if let Writing Style (Concise)
if let Some(value) = optional {
    println!("The value is: {}", value);
    // No need to write _ => ()
}

(4) مرجع سريع لكلمات مفتاحية تدفق التحكم

الكلمة الرئيسية/الصيغة الغرض مثال
if / else if / else التفرع الشرطي if x > 0 { ... } else { ... }
match مطابقة الأنماط match x { 1 => ..., _ => ... }
if let سكر نحوي مطابق أحادي الوضع if let Some(v) = x { ... }
while الحلقات الشرطية while x > 0 { ... }
while let حلقة مطابقة الأنماط while let Some(v) = iter.next() { ... }
loop حلقة لا نهائية loop { ... break; }
التنقل عبر المكرر
break الخروج من الحلقة break; أو break value;
continue تخطي هذه الدورة continue;


6. مثال كامل

(1) ▶ المثال:تصنيف الدرجات باستخدام if/else (مستوى الصعوبة ⭐)

RUST
// ============================================
// Use if expression to grade scores and return a string
// ============================================

fn main() {
    let score = 88;

    let grade = if score >= 90 {
        "Excellent (A)"
    } else if score >= 80 {
        "Good (B)"
    } else if score >= 70 {
        "Intermediate (C)"
    } else if score >= 60 {
        "Passing Grade (D)"
    } else {
        "Fail (F)"
    };

    println!("Fractions: {}, Level: {}", score, grade);
}

الناتج:

TEXT 📖 للعرض فقط
Fractions: 88, Level: Good (B)

if يجب أن تُرجع كل فرع من فروع التعبير نفس النوع. وهنا، تُرجع جميع الفروع &str، لذا فإن المُترجم راضٍ عن ذلك.


(2) ▶ المثال:استخدام match للتعامل مع رموز حالة HTTP (مستوى الصعوبة: ⭐⭐)

RUST
// ============================================
// Use match to gracefully handle HTTP Status Codes
// ============================================

fn handle_status_code(code: u16) -> &'static str {
    match code {
        200 => "OK: Request Successful",
        201 => "Created: The resource has been created",
        204 => "No Content: No results found",
        301 | 302 => "Redirect: The resource has been moved",
        400 => "Bad Request: Invalid request format",
        401 => "Unauthorized: Unauthorized",
        403 => "Forbidden: Access Denied",
        404 => "Not Found: Resource does not exist",
        500 => "Internal Server Error: Internal Server Error",
        502 | 503 => "Server Error: The server is temporarily unavailable",
        _ => "Unknown: Unknown status code",
    }
}

fn main() {
    let codes = [200, 404, 418, 500];
    for &code in codes.iter() {
        println!("Status Code {}: {}", code, handle_status_code(code));
    }
}

الناتج:

TEXT 📖 للعرض فقط
Status Code 200: OK: Request Successful
Status Code 404: Not Found: Resource does not exist
Status Code 418: Unknown: Unknown status code
Status Code 500: Internal Server Error: Internal Server Error

| يتيح هذا المشغل للمفرع مطابقة أنماط متعددة، مما يمنع تكرار الكود. _ تضمن أحرف البدل تغطية جميع رموز الحالة.


(3) ▶ المثال:حراس المطابقة و if let (الصعوبة ⭐⭐)

RUST
// ============================================
// match Guard + if let Simplified Pattern Matching
// ============================================

fn main() {
    let number = Some(42);

    // match Guard: Add additional conditions to the pattern
    match number {
        Some(x) if x > 100 => println!("Very Large Numbers: {}", x),
        Some(x) if x > 50  => println!("Big Numbers: {}", x),
        Some(x)             => println!("Small numbers: {}", x),
        None                => println!("No numbers"),
    }

    // if let Syntactic sugar -- Only care about the Some case
    let value = Some("Rust");
    if let Some(lang) = value {
        println!("Language is: {}", lang);
    }

    // if let with else
    let empty: Option<i32> = None;
    if let Some(x) = empty {
        println!("Not null: {}", x);
    } else {
        println!("No value");
    }
}

الناتج:

TEXT 📖 للعرض فقط
Small numbers: 42
Language is: Rust
No value

استخدم الكلمة الرئيسية if في عبارة المطابقة لإضافة شرط إضافي بعد النمط — ولن يتم الدخول إلى هذا الفرع إلا إذا تطابق النمط وكان الشرط صحيحًا. وتُعد if let مناسبة للحالات التي «لا تهتم فيها إلا بنمط واحد».


(4) ▶ المثال:استخدام match لتفكيك القوائم التعدادية والمجموعات (مستوى الصعوبة: ⭐⭐⭐)

RUST
// ============================================
// match Deconstruction: Enumeration + Tuple + Guard
// ============================================

enum Shape {
    Circle(f64),
    Rectangle(f64, f64),
    Triangle(f64, f64, f64),
}

fn describe_shape(shape: &Shape) -> String {
    match shape {
        Shape::Circle(radius) if *radius > 10.0 => {
            format!("Big Circle (Radius {:.1})", radius)
        }
        Shape::Circle(radius) => {
            format!("Small Circle (Radius {:.1})", radius)
        }
        Shape::Rectangle(w, h) if w == h => {
            format!("Square (Side length {:.1})", w)
        }
        Shape::Rectangle(w, h) => {
            format!("Rectangle (Width {:.1}, Height {:.1})", w, h)
        }
        Shape::Triangle(a, b, c) => {
            format!("Triangle (Sides {:.1}, {:.1}, {:.1})", a, b, c)
        }
    }
}

fn main() {
    let shapes = [
        Shape::Circle(5.0),
        Shape::Circle(15.0),
        Shape::Rectangle(4.0, 4.0),
        Shape::Rectangle(3.0, 5.0),
        Shape::Triangle(3.0, 4.0, 5.0),
    ];

    println!("=== Graphical Description ===");
    for shape in &shapes {
        println!("{}", describe_shape(shape));
    }

    println!("\n=== Coordinate Classification ===");
    let points: [(i32, i32); 4] = [(0, 0), (3, 0), (0, -2), (5, 7)];
    for point in points {
        let desc = match point {
            (0, 0) => "Origin".to_string(),
            (x, 0) => format!("On x-axis (x={})", x),
            (0, y) => format!("On y-axis (y={})", y),
            (x, y) if x > 0 && y > 0 => format!("First Quadrant ({}, {})", x, y),
            (x, y) => format!("Other Quadrants ({}, {})", x, y),
        };
        println!("Point {:?}: {}", point, desc);
    }
}

الناتج:

TEXT 📖 للعرض فقط
=== Graphical Description ===
Small Circle (Radius 5.0)
Big Circle (Radius 15.0)
Square (Side length 4.0)
Rectangle (Width 3.0, Height 5.0)
Triangle (Sides 3.0, 4.0, 5.0)

=== Coordinate Classification ===
Point (0, 0): Origin
Point (3, 0): On x-axis (x=3)
Point (0, -2): On y-axis (y=-2)
Point (5, 7): First Quadrant (5, 7)

تتيح لك إمكانيات تفكيك البنية في match إمكانية مطابقة متغيرات التعداد واستخراج القيم الداخلية وتطبيق شروط الحراسة في آن واحد، من أجل تنفيذ منطق تصنيف دقيق. ويضمن الفحص الشامل الذي يقوم به المُجمِّع ألا تغفل أي حالة.


(5) ▶ المثال:التطبيق العملي لـ if let و while let (مستوى الصعوبة: ⭐⭐)

RUST
// ============================================
// if let and while let: Simplify Option/Result Processing
// ============================================

fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 {
        None
    } else {
        Some(a / b)
    }
}

fn main() {
    println!("=== if let Processing Option ===");
    let results = [divide(10.0, 2.0), divide(8.0, 0.0), divide(15.0, 3.0)];

    for result in &results {
        if let Some(value) = result {
            println!("Division Successful: {:.2}", value);
        } else {
            println!("Division Failed: Divisor is zero");
        }
    }

    println!("\n=== if let Chain Processing ===");
    let alice_input: Option<&str> = Some("42");
    let bob_input: Option<&str> = None;

        if let Some(num_str) = alice_input {
        if let Ok(num) = num_str.parse::<i32>() {
            println!("Alice's number: {}", num);
        }
    }

    if let Some(num_str) = bob_input {
        if let Ok(num) = num_str.parse::<i32>() {
            println!("Bob's number: {}", num);
        }
    } else {
        println!("Bob has no data input");
    }

    println!("\n=== while let Simulation Iteration ===");
    let mut stack = vec![3, 2, 1];
    while let Some(top) = stack.pop() {
        println!("Popped: {}", top);
    }
    println!("The stack is empty");
}

الناتج:

TEXT 📖 للعرض فقط
=== if let Processing Option ===
Division Successful: 5.00
Division Failed: Divisor is zero
Division Successful: 5.00

=== if let Chain Processing ===
Alice's number: 42
Bob has no data input

=== while let Simulation Iteration ===
Popped: 1
Popped: 2
Popped: 3
The stack is empty

if let مناسب للحالات التي لا يهمك فيها سوى Some/Ok ويمكنك تجاهل الحالات الأخرى؛ أما while let فهو مناسب للحالات التي تقوم فيها بالتكرار عبر القيم حتى تصادف None (مثل عمليات المكدس pop()). وكلاهما أكثر إيجازًا من match الكامل.



❓ أسئلة شائعة

س ما الفرق بين match وswitch؟
ج match أقوى بكثير من switch.
س لماذا يعتبر الفحص الشامل match مهمًا جدًّا؟
ج لأنه يمثل «مساعدة المُجمِّع لك في اكتشاف الأخطاء».
س متى يجب استخدام if let؟
ج عندما تكون مهتمًّا بنمط واحد فقط.
س ما الفرق بين حرف البدل _ واسم المتغير في match؟
ج يتجاهل الحرف _ القيمة تمامًا ولا يربطها، بينما يربط اسم المتغير القيمة ولكنه قد يؤدي إلى ظهور تحذير من المُجمِّع بشأن المتغيرات غير المستخدمة.
س ماذا أفعل إذا كان فرعا تعبير if من نوعين مختلفين؟
ج سيحدث خطأ في الترجمة.

📖 ملخص


📝 تمارين

  1. الصعوبة ⭐: اكتب دالة fn number_to_word(n: i32) -> &'static str تستخدم match لتحويل الأرقام من 0 إلى 5 إلى الكلمات الإنجليزية المقابلة لها (zero/one/two/three/four/five)، وتُرجع "unknown" لجميع الأرقام الأخرى.
  2. الصعوبة ⭐⭐: قم بتعريف قائمة عدّية enum TrafficLight { Red, Yellow, Green } واستخدم match لإرجاع «وقت الانتظار» المقابل لكل لون (الأحمر: 30 ثانية، الأصفر: 3 ثوانٍ، الأخضر: 45 ثانية).
  3. الصعوبة ⭐⭐⭐ : اكتب دالة fn describe_point(point: (i32, i32)) تستخدم match لمعالجة نقطة إحداثيات ثنائية الأبعاد: إذا كانت النقطة تقع عند نقطة الأصل (0,0)، فقم بإخراج "نقطة الأصل"؛ وإذا كانت على المحور x (x,0)، فاطرح "على المحور x"؛ وإذا كانت على المحور y (0,y)، فاطرح "على المحور y"؛ وفي جميع الحالات الأخرى، اطرح قيم الإحداثيات.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%