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

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

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

والآن بعد أن أصبح بإمكانك تخزين أنواع مختلفة في Vec<Box<dyn Trait>>، تتمثل الخطوة التالية في معالجة collection. تعمل عملية التكرار على كائنات السمات تمامًا مثل التكرار على أي متجه. تكرّر عبر كل عنصر وتستدعي الأساليب التي تحددها السمة.

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 مباشرةً: يتولى Rust الإرسال الديناميكي تلقائيًا، ويستدعي التنفيذ الصحيح لكل نوع ملموس.

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

challenge icon

التحدي

سهل

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

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

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

تتجلّى قوة كائنات السمات هنا. لا تحتاج شيفرة التكرار إلى معرفة ما إذا كانت تعالج 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: كرّر عبر المتجه واحسب السعر الإجمالي

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

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

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

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