Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

مطابقة حالات البيانات

جزء من قسم Object Oriented Programming في رحلة Rust على Coddy — الدرس 16 من 61.

في الدرس السابق، استخدمت match داخل توابع الـ enum للتعامل مع المتغيرات المختلفة. الآن دعونا نركز على كيفية قيام match باستخراج البيانات المخزنة داخل تلك المتغيرات فعلياً—وهي تقنية تسمى destructuring.

عندما تقوم بالمطابقة على نوع من أنواع التعداد (enum variant) الذي يحتوي على بيانات، يمكنك ربط تلك البيانات بمتغيرات مباشرة داخل النمط:

enum Event {
    Click { x: i32, y: i32 },
    KeyPress(char),
    Resize(u32, u32),
}

fn handle_event(event: Event) {
    match event {
        Event::Click { x, y } => {
            println!("Clicked at ({}, {})", x, y);
        }
        Event::KeyPress(key) => {
            println!("Key pressed: {}", key);
        }
        Event::Resize(width, height) => {
            println!("Resized to {}x{}", width, height);
        }
    }
}

يستخرج كل ذراع البيانات بشكل مختلف بناءً على بنية المتغير. بالنسبة للمتغيرات الشبيهة بالهياكل (struct-like)، تستخدم { field_name } لاستخراج الحقول المسماة. أما بالنسبة لمتغيرات الصفوف (tuple)، فتستخدم متغيرات موضعية مثل (width, height).

تصبح أسماء المتغيرات التي تختارها متاحة داخل ذراع المطابقة (match arm) هذا. هذا أمر قوي لأنك تتحقق في الوقت نفسه من النوع (variant) الذي تملكه و تحصل على حق الوصول إلى البيانات المرتبطة به (payload).

يضمن المترجم أنك تتعامل مع كل حالة، وتكون القيم المستخرجة جاهزة للاستخدام فوراً - دون الحاجة لخطوات إضافية.

challenge icon

التحدي

سهل

لنقم ببناء معالج أوامر يتعامل مع أنواع مختلفة من أوامر المستخدم. يحمل كل أمر بيانات مختلفة، وسيقوم المعالج الخاص بك باستخدام مطابقة الأنماط (pattern matching) لاستخراج تلك البيانات وإنتاج الاستجابات المناسبة.

ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:

  • command.rs: قم بتعريف enum عام باسم Command بثلاثة متغيرات (variants):
    • Say — يحمل رسالة String واحدة (صيغة tuple)
    • Move — يحمل حقولاً مسماة: direction (String) و steps (u32)
    • Calculate — يحمل قيمتين من نوع i32 تمثلان الأرقام المراد جمعها (صيغة tuple)
    ثم قم بتنفيذ تابع execute يستخدم match لتفكيك كل متغير وإرجاع String يصف الإجراء المتخذ.
  • main.rs: قم باستيراد وحدة الأوامر الخاصة بك، وأنشئ نسخة واحدة من كل متغير من متغيرات الأوامر باستخدام المدخلات المقدمة، واستدعِ تابع execute على كل منها لطباعة النتائج.

يجب أن يقوم تابع execute الخاص بك بتفكيك كل متغير للوصول إلى بياناته وإرجاع الرسائل بهذه التنسيقات الدقيقة:

  • لـ Say: Saying: {message}
  • لـ Move: Moving {steps} steps {direction}
  • لـ Calculate: {a} + {b} = {sum}

يجب أن يعرض مخرجك نتيجة كل أمر في سطر مستقل:

Saying: {message}
Moving {steps} steps {direction}
{a} + {b} = {sum}

على سبيل المثال، مع رسالة say نصها "Hello world"، وتحرك بمقدار 5 خطوات "north"، وعملية حسابية لـ 10 و 25، سيكون المخرج:

Saying: Hello world
Moving 5 steps north
10 + 25 = 35

ستتلقى خمسة مدخلات: رسالة say، والاتجاه، وعدد الخطوات، والرقمين المطلوب حسابهما.

ورقة مرجعية

تسمح لك مطابقة الأنماط باستخدام match بتفكيك متغيرات التعداد (enum variants) واستخراج بياناتها مباشرة في النمط.

بالنسبة لـ متغيرات الصف (tuple variants)، استخدم المتغيرات الموضعية:

enum Event {
    KeyPress(char),
    Resize(u32, u32),
}

match event {
    Event::KeyPress(key) => {
        println!("Key: {}", key);
    }
    Event::Resize(width, height) => {
        println!("{}x{}", width, height);
    }
}

بالنسبة لـ المتغيرات الشبيهة بالهياكل (struct-like variants)، استخدم صيغة الحقول المسماة:

enum Event {
    Click { x: i32, y: i32 },
}

match event {
    Event::Click { x, y } => {
        println!("Clicked at ({}, {})", x, y);
    }
}

تصبح المتغيرات المستخرجة متاحة داخل ذراع المطابقة (match arm) الخاص بها، مما يسمح لك بالتحقق من نوع المتغير والوصول إلى بياناته في آن واحد.

جرّب بنفسك

mod command;

use command::Command;

fn main() {
    // قراءة المدخلات
    let mut say_message = String::new();
    std::io::stdin().read_line(&mut say_message).expect("Failed to read line");
    let say_message = say_message.trim().to_string();

    let mut direction = String::new();
    std::io::stdin().read_line(&mut direction).expect("Failed to read line");
    let direction = direction.trim().to_string();

    let mut steps_input = String::new();
    std::io::stdin().read_line(&mut steps_input).expect("Failed to read line");
    let steps: u32 = steps_input.trim().parse().expect("Failed to parse steps");

    let mut num1_input = String::new();
    std::io::stdin().read_line(&mut num1_input).expect("Failed to read line");
    let num1: i32 = num1_input.trim().parse().expect("Failed to parse num1");

    let mut num2_input = String::new();
    std::io::stdin().read_line(&mut num2_input).expect("Failed to read line");
    let num2: i32 = num2_input.trim().parse().expect("Failed to parse num2");

    // TODO: قم بإنشاء أمر Say باستخدام say_message وقم بتنفيذه
    
    // TODO: قم بإنشاء أمر Move باستخدام direction و steps، وقم بتنفيذه
    
    // TODO: قم بإنشاء أمر Calculate باستخدام num1 و num2، وقم بتنفيذه
    
    // TODO: اطبع نتائج تنفيذ كل أمر
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming