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

التكرار عبر كائنات Trait

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

الآن بعد أن أصبح بإمكانك تخزين أنواع مختلفة في Vec<Box<dyn Trait>>، فإن الخطوة التالية هي معالجة تلك المجموعة. يعمل التكرار عبر كائنات السمة (trait objects) تماماً مثل التكرار عبر أي متجه (vector) - حيث تقوم بالمرور عبر كل عنصر واستدعاء الطرق (methods) المحددة بواسطة السمة.

trait Shape {
    fn area(&self) -> f64;
}

struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }

impl Shape for Circle {
    fn area(&self) -> f64 { 3.14159 * self.radius * self.radius }
}

impl Shape for Rectangle {
    fn area(&self) -> f64 { self.width * self.height }
}

fn main() {
    let shapes: Vec<Box<dyn Shape>> = vec![
        Box::new(Circle { radius: 2.0 }),
        Box::new(Rectangle { width: 3.0, height: 4.0 }),
        Box::new(Circle { radius: 1.0 }),
    ];

    for shape in &shapes {
        println!("Area: {}", shape.area());
    }
}

عندما تقوم بالتكرار باستخدام for shape in &shapes، يكون كل shape مرجعاً لـ Box<dyn Shape>. يمكنك استدعاء أي طريقة (method) من الـ Shape trait مباشرةً—تتعامل لغة Rust مع الإرسال الديناميكي (dynamic dispatch) تلقائياً، حيث تستدعي التنفيذ الصحيح لكل نوع ملموس (concrete type).

هذا النمط قوي لتجميع النتائج. يمكنك جمع كل المساحات، أو تصفية الأشكال حسب الحجم، أو تحويل المجموعة. الفكرة الجوهرية هي أنه بمجرد أن تصبح العناصر في مجموعة من الـ trait objects، فإنك تتفاعل معها حصرياً من خلال واجهة الـ trait، بغض النظر عن أنواعها الأساسية.

challenge icon

التحدي

سهل

لنقم ببناء حاسبة أسعار لعربة تسوق يمكنها استيعاب أنواع مختلفة من العناصر! ستقوم بإنشاء نظام حيث يمكن تخزين المنتجات والخدمات — ولكل منها منطق تسعير مختلف — معاً ومعالجتها لحساب السعر الإجمالي.

ستقوم بتنظيم الكود الخاص بك عبر ملفين:

  • items.rs: قم بتعريف سمة (trait) عامة باسم Priceable مع طريقة price تأخذ &self وتعيد f64. ثم قم بإنشاء هيكلين (structs) عامين:
    • Product — مع حقول عامة name (String) و cost (f64). يجب أن تعيد طريقة price الخاصة به ببساطة الـ cost.
    • Service — مع حقول عامة name (String) و hourly_rate (f64) و hours (f64). يجب أن تعيد طريقة price الخاصة به hourly_rate * hours.
  • main.rs: قم باستيراد وحدة العناصر (items module) الخاصة بك وأنشئ متجهاً (vector) من نوع Vec<Box<dyn Priceable>>. استخدم المدخلات لإنشاء Product واحد و Service واحدة، وأضفهما إلى المتجه الخاص بك، ثم قم بالمرور عبر المجموعة لحساب وطباعة السعر الإجمالي لجميع العناصر.

تتجلى قوة كائنات السمات (trait objects) هنا — لا يحتاج كود التكرار الخاص بك إلى معرفة ما إذا كان يعالج Product أو Service. إنه ببساطة يستدعي price() على كل عنصر، ويقوم Rust بتوجيه الاستدعاء إلى التنفيذ الصحيح تلقائياً.

يجب أن يعرض مخرجك السعر الإجمالي مع منزلتين عشريتين:

Total: ${total}

على سبيل المثال، مع المدخلات Laptop، 999.99، Consulting، 150.0، و 3.0:

Total: $1449.99

ومع المدخلات Book، 29.99، Tutoring، 50.0، و 2.0:

Total: $129.99

ستتلقى خمسة مدخلات: اسم المنتج، تكلفة المنتج، اسم الخدمة، معدل الأجر بالساعة، وعدد ساعات العمل (قم بتحليل القيم الرقمية كـ f64).

جرّب بنفسك

mod items;

use items::{Priceable, Product, Service};

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

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

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

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

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

    // TODO: إنشاء Product باستخدام product_name و product_cost

    // TODO: إنشاء Service باستخدام service_name و hourly_rate و hours

    // TODO: إنشاء Vec<Box<dyn Priceable>> وإضافة كلا العنصرين إليه

    // TODO: التكرار عبر الـ vector وحساب السعر الإجمالي

    // TODO: طباعة المجموع مع منزلة عشرية واحدة
    // التنسيق: Total: ${total:.1}
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming