التنفيذات الافتراضية
جزء من قسم Object Oriented Programming في رحلة Rust على Coddy — الدرس 31 من 61.
حتى الآن، كان كل أسلوب سمة (trait method) قمنا بتعريفه مجرد توقيع—متطلب بدون جسم. ولكن ماذا لو كانت معظم الأنواع التي تطبق سمة ما ستستخدم نفس المنطق؟ تتيح لك Rust توفير تنفيذ افتراضي مباشرة في تعريف السمة.
بدلاً من إنهاء توقيع الدالة بفاصلة منقوطة، يمكنك إضافة جسم للدالة:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
الآن، أي نوع يقوم بتنفيذ Greet يحصل تلقائياً على هذه الدالة — دون الحاجة لكتابة أي كود داخل كتلة impl:
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // أهلاً بك!
إنّ impl Greet for Guest {} الفارغة صالحة لأن السمة (trait) توفر السلوك بالفعل. تقوم البنية (struct) ببساطة بـ "الموافقة" على السمة وتتلقى الدالة الافتراضية مجانًا.
هذا النمط مفيد عندما تريد تعريف سلوك مشترك يعمل في معظم الحالات. الأنواع التي تحتاج إلى شيء مختلف يمكنها تجاوز السلوك الافتراضي (والذي ستتعلمه لاحقاً)، بينما الأنواع التي تكتفي بالسلوك القياسي لا تحتاج إلى تكرار نفسها.
التحدي
سهلدعونا نستكشف قوة التنفيذات الافتراضية (default implementations) من خلال بناء نظام تنبيهات! ستقوم بإنشاء سمة (trait) توفر سلوكاً قياسياً جاهزاً، بحيث يمكن للأنواع الاستفادة منه دون كتابة كود متكرر.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
notifiable.rs: قم بتعريف سمة عامةNotifiableمع طريقةnotify(&self) -> Stringتحتوي على تنفيذ افتراضي. يجب أن يعيد التنفيذ الافتراضي"You have a new notification!". أي نوع يقوم بتنفيذ هذه السمة سيحصل تلقائياً على هذا السلوك دون الحاجة لكتابة أي شيء داخل كتلة impl الخاصة به.alerts.rs: قم بإنشاء هيكلين بسيطين (unit structs) عامين ينفذان السمة الخاصة بك:EmailAlert— ينفذNotifiableبكتلة impl فارغة (يستخدم الافتراضي)SystemAlert— ينفذ أيضاًNotifiableبكتلة impl فارغة (يستخدم الافتراضي)
main.rs: اجمع وحداتك معاً واستعرض كيف يتشارك كلا نوعي التنبيهات نفس السلوك الافتراضي. قم بإنشاء مثيلات (instances) من كلا الهيكلين واستدعِ طرقnotifyالخاصة بهما.
في ملفك الرئيسي، قم بإنشاء EmailAlert و SystemAlert، ثم اطبع تنبيهاتهما في أسطر منفصلة.
يجب أن يكون مخرجك:
You have a new notification!
You have a new notification!لاحظ كيف ينتج كلا النوعين مخرجات متطابقة—هذا هو جمال التنفيذات الافتراضية! لم يحتج أي من الهيكلين لكتابة أي كود لطريقة notify، ومع ذلك يمتلك كلاهما سلوك تنبيه يعمل بكامل طاقته. في الدرس القادم، ستتعلم كيفية تجاوز (override) هذه الافتراضات عندما يحتاج نوع ما إلى سلوك مخصص.
ورقة مرجعية
يمكن أن تمتلك دوال الـ Trait تطبيقات افتراضية عبر توفير جسم للدالة بدلاً من مجرد التوقيع:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
تتلقى الأنواع التي تطبق الـ trait السلوك الافتراضي تلقائياً دون الحاجة لكتابة أي كود:
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // Hello there!
تعتبر كتلة impl الفارغة صالحة عندما يوفر الـ trait تطبيقات افتراضية. يسمح هذا للأنواع بالانضمام إلى السلوك المشترك دون تكرار.
جرّب بنفسك
mod notifiable;
mod alerts;
use notifiable::Notifiable;
use alerts::{EmailAlert, SystemAlert};
fn main() {
// TODO: إنشاء مثيل لـ EmailAlert
// TODO: إنشاء مثيل لـ SystemAlert
// TODO: طباعة الإشعار من EmailAlert
// TODO: طباعة الإشعار من SystemAlert
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1الأساليب والسلوك
مقدمة في كتل التنفيذمعامل Selfالأساليب القابلة للتعديلالدوال المرتبطةكتل تنفيذ متعددةتسلسل الأساليبمراجعة - إجراءات المستطيل4مشروع: حيوان أليف افتراضي
تعريف الحيوان الأليفإطعام الحيوان الأليف2التغليف والوحدات البرمجية
أساسيات الوحدات البرمجيةالكلمة المفتاحية Publicالحقول الخاصةدوال Gettersدوال Settersمراجعة - الخزانة الآمنة5الأنواع العامة (Generics)
الـ Structs العامةالـ Methods العامةأنواع عامة متعددةالـ Functions العامةمراجعة - Coordinate Point3التعدادات المتقدمة (Advanced Enums)
التعدادات مع البياناتالأساليب (Methods) في التعداداتمطابقة حالات البياناتمراجعة التعداد Optionملخص - التعداد Shape6تعريف الـ Traits
ما هي الـ Trait؟تنفيذ الـ Traitsالتنفيذات الافتراضيةتجاوز التنفيذات الافتراضيةالـ Traits مع المعاملاتملخص - مشغل الوسائط