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

متجهات من الـ Traits

جزء من قسم البرمجة كائنية التوجه في رحلة Rust على Coddy. الدرس 46 من 61.

لقد رأيت كيف يتيح Box<dyn Trait> لمتغير واحد الاحتفاظ بأنواع مختلفة. تظهر القوة الحقيقية عند دمج ذلك مع المجموعات: تخزين أنواع مختلفة متعددة في متجه واحد.

يتطلب المتجه العادي أن تكون جميع العناصر من النوع نفسه. لا يمكنك امتلاك Vec<Dog> يحتوي أيضًا على قطط. لكن باستخدام كائنات السمات، يمكنك إنشاء متجه يحتفظ بأي شيء يطبّق سمة مشتركة:

trait Speak {
    fn speak(&self) -> String;
}

struct Dog;
struct Cat;
struct Bird;

impl Speak for Dog {
    fn speak(&self) -> String { String::from("Woof!") }
}

impl Speak for Cat {
    fn speak(&self) -> String { String::from("Meow!") }
}

impl Speak for Bird {
    fn speak(&self) -> String { String::from("Tweet!") }
}

fn main() {
    let animals: Vec<Box<dyn Speak>> = vec![
        Box::new(Dog),
        Box::new(Cat),
        Box::new(Bird),
    ];
}

النوع Vec<Box<dyn Speak>> يعني «متجهًا من كائنات السمات المغلّفة». كل عنصر هو Box يشير إلى شيء ينفّذ Speak: يمكن أن تختلف الأنواع الفعلية. تغلّف كل قيمة باستخدام Box::new() عند إضافتها إلى المتجه.

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

challenge icon

التحدي

سهل

لنَبْنِ نظام إشعارات يمكنه التعامل مع أنواع مختلفة من التنبيهات! ستنشئ مجموعة تخزّن أنواعًا مختلفة من الإشعارات، ورسائل البريد الإلكتروني، ورسائل SMS، والإشعارات الفورية، كلها في متجه واحد باستخدام كائنات السمات.

ستنظّم شيفرتك عبر ملفين:

  • notifications.rs: عرّف سمة عامة Notify تحتوي على طريقة send تأخذ &self وتُرجع قيمة من النوع String تصف الإشعار الذي يتم إرساله. ثم أنشئ ثلاثة هياكل عامة:
    • Email: تحتوي على حقل عام recipient (من النوع String). يجب أن تُرجع send القيمة Email to: {recipient}
    • Sms: تحتوي على حقل عام phone (من النوع String). يجب أن تُرجع send القيمة SMS to: {phone}
    • Push: تحتوي على حقل عام device (من النوع String). يجب أن تُرجع send القيمة Push to: {device}
  • main.rs: أضف وحدة notifications وأنشئ متجهًا من النوع Vec<Box<dyn Notify>> يحتفظ بأنواع الإشعارات الثلاثة كلها. استخدم المدخلات المُقدَّمة لإنشاء إشعار واحد من كل نوع، وأضفها إلى متجهك بالترتيب (Email، Sms، Push)، ثم اطبع نتيجة استدعاء send() على العنصر الأول من المتجه.

المفهوم الأساسي هنا هو أنه رغم كون Email وSms وPush هياكل مختلفة تمامًا، يمكنها جميعًا أن تعيش معًا في المتجه نفسه لأنها تشترك في سمة Notify. يُغلَّف كل عنصر باستخدام Box::new() لإنشاء كائن سمة.

يجب أن يعرض ناتجك الإشعار الأول:

Email to: {recipient}

على سبيل المثال، عند استخدام المدخلات alice@example.com و555-1234 وiPhone-12:

Email to: alice@example.com

ستتلقى ثلاث مدخلات: مستلم البريد الإلكتروني، ورقم الهاتف، واسم الجهاز.

جرّب بنفسك

mod notifications;

use notifications::{Notify, Email, Sms, Push};

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

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

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

    // TODO: أنشئ Vec<Box<dyn Notify>> لحفظ أنواع الإشعارات المختلفة
    // TODO: أنشئ مثيلات Email و Sms و Push باستخدام المدخلات
    // TODO: أضفها إلى المتجه بالترتيب (Email, Sms, Push) باستخدام Box::new()
    // TODO: اطبع نتيجة استدعاء send() على العنصر الأول
}
quiz iconاختبر نفسك

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

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم Rust عبر الإنترنت