الإرسال الديناميكي (Dynamic Dispatch)
جزء من قسم البرمجة كائنية التوجه في رحلة Rust على Coddy. الدرس 45 من 61.
عند استخدام الأنواع العامة مع قيود السمات مثل <T: Summary>، تحدد Rust النوع الدقيق في وقت الترجمة. يُسمى هذا الإرسال الساكن: ينشئ المترجم تعليمات برمجية متخصصة لكل نوع ملموس تستخدمه. إنه سريع، لكن هناك قيدًا: لا يمكن للمتغير أن يحتفظ إلا بنوع محدد واحد.
ماذا لو احتجت إلى متغير واحد يمكنه الاحتفاظ بأنواع مختلفة أثناء التشغيل؟ هنا يأتي دور كائنات السمات. باستخدام Box<dyn Trait>، يمكنك تخزين أي نوع ينفّذ السمة:
trait Speak {
fn speak(&self) -> String;
}
struct Dog;
struct Cat;
impl Speak for Dog {
fn speak(&self) -> String { String::from("Woof!") }
}
impl Speak for Cat {
fn speak(&self) -> String { String::from("Meow!") }
}
fn main() {
let animal: Box<dyn Speak> = Box::new(Dog);
println!("{}", animal.speak()); // "Woof!"
let animal: Box<dyn Speak> = Box::new(Cat);
println!("{}", animal.speak()); // "Meow!"
}
تشير الكلمة المفتاحية dyn إلى التوزيع الديناميكي: تبحث Rust عن الطريقة التي يجب استدعاؤها في وقت التشغيل بدلًا من وقت الترجمة. تُعد Box ضرورية لأن كائنات السمة ليس لها حجم معروف؛ إذ توفر Box مؤشرًا ذا حجم ثابت.
فكّر في الأمر بهذه الطريقة: تقول الأنواع العامة «أعمل مع النوع T»، بينما تقول كائنات السمات «أعمل مع أي شيء يمكنه فعل ذلك». والمقابل هو تكلفة صغيرة أثناء التشغيل مقابل المرونة في التعامل مع أنواع مختلفة من خلال المتغير نفسه.
التحدي
سهللنَبْنِ نظامًا لتأجير المركبات يوضّح قوة الإرسال الديناميكي! ستنشئ سمةً تحدد ما يمكن لأي مركبة قابلة للتأجير القيام به، ثم تستخدم Box<dyn Trait> لتخزين أنواع مختلفة من المركبات في المتغير نفسه.
ستنظّم شفرتك عبر ملفين:
vehicle.rs: عرّف سمةً عامةRentableباستخدام طريقةrental_infoالتي تأخذ&selfوتُرجع قيمة من النوعString. ثم أنشئ بنيتين عامتين:Car: تحتوي على حقل عامmodel(من النوع String). يجب أن تُرجعrental_infoالقيمةCar: {model}Bike: تحتوي على حقل عامbrand(من النوع String). يجب أن تُرجعrental_infoالقيمةBike: {brand}
main.rs: استورد وحدة المركبات واستخدم المدخلات لتوضيح الإرسال الديناميكي. أنشئ متغيرًا من النوعBox<dyn Rentable>يحتفظ أولًا بكائنCar، واطبع معلومات التأجير الخاصة به، ثم أعد إسناد المتغير نفسه ليحتفظ بكائنBikeواطبع معلومات التأجير الخاصة به مرة أخرى.
تكمن الفكرة الرائعة هنا في أن متغيرًا واحدًا من النوع Box<dyn Rentable> يمكنه الاحتفاظ إما بكائن Car أو بكائن Bike: إذ تحدد Rust أي طريقة rental_info يجب استدعاؤها أثناء التشغيل. هذا هو الإرسال الديناميكي في الواقع!
يجب أن يعرض ناتجك معلومات كلتا المركبتين:
Car: {model}
Bike: {brand}على سبيل المثال، عند استخدام المدخلين Tesla Model 3 وTrek:
Car: Tesla Model 3
Bike: Trekستتلقى مدخلين: طراز السيارة والعلامة التجارية للدراجة.
جرّب بنفسك
mod vehicle;
use vehicle::{Rentable, Car, Bike};
fn main() {
// اقرأ الإدخال
let mut car_model = String::new();
std::io::stdin().read_line(&mut car_model).expect("Failed to read line");
let car_model = car_model.trim().to_string();
let mut bike_brand = String::new();
std::io::stdin().read_line(&mut bike_brand).expect("Failed to read line");
let bike_brand = bike_brand.trim().to_string();
// TODO: أنشئ متغيرًا من نوع Box<dyn Rentable> يحمل Car
// اطبع معلومات الإيجار الخاصة به باستخدام الدالة rental_info()
// TODO: أعد تعيين نفس المتغير ليحمل Bike
// اطبع معلومات الإيجار الخاصة به مرة أخرى
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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 عبر الإنترنت