متجهات من الـ 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() عند إضافتها إلى المتجه.
هذا النمط ضروري لبناء أنظمة مرنة تحتاج فيها إلى إدارة مجموعات من الكائنات المترابطة ولكن المختلفة، مثل مكوّنات واجهة المستخدم أو كيانات الألعاب أو أنظمة الإضافات.
التحدي
سهللنَبْنِ نظام إشعارات يمكنه التعامل مع أنواع مختلفة من التنبيهات! ستنشئ مجموعة تخزّن أنواعًا مختلفة من الإشعارات، ورسائل البريد الإلكتروني، ورسائل 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() على العنصر الأول
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1الأساليب والسلوك
مقدمة في كتل التنفيذمعامل Selfالأساليب القابلة للتعديلالدوال المرتبطةكتل تنفيذ متعددةتسلسل الأساليبمراجعة - إجراءات المستطيل4مشروع: حيوان أليف افتراضي
تعريف الحيوان الأليفإطعام الحيوان الأليف2التغليف والوحدات البرمجية
أساسيات الوحدات البرمجيةالكلمة المفتاحية Publicالحقول الخاصةدوال Gettersدوال Settersمراجعة - الخزانة الآمنة5الأنواع العامة (Generics)
الـ Structs العامةالـ Methods العامةأنواع عامة متعددةالـ Functions العامةمراجعة - Coordinate Point3التعدادات المتقدمة (Advanced Enums)
التعدادات مع البياناتالأساليب (Methods) في التعداداتمطابقة حالات البياناتمراجعة التعداد Optionملخص - التعداد Shape6تعريف الـ Traits
ما هي الـ Trait؟تنفيذ الـ Traitsالتنفيذات الافتراضيةتجاوز التنفيذات الافتراضيةالـ Traits مع المعاملاتملخص - مشغل الوسائط9كائنات Trait
الإرسال الديناميكي (Dynamic Dispatch)متجهات من الـ Traitsالتكرار عبر كائنات Traitمراجعة - مدير حديقة الحيوانتدرّب بنفسك: مترجم Rust عبر الإنترنت