التكرار عبر كائنات 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 الإرسال الديناميكي تلقائيًا، ويستدعي التنفيذ الصحيح لكل نوع ملموس.
هذا النمط فعّال لتجميع النتائج. يمكنك جمع كل المساحات، أو تصفية الأشكال حسب الحجم، أو تحويل المجموعة. تتمثل الفكرة الأساسية في أنه بمجرد أن تصبح العناصر ضمن مجموعة من كائنات السمة، فإنك تتفاعل معها حصريًا من خلال واجهة السمة، بغض النظر عن أنواعها الأساسية.
التحدي
سهللنبنِ حاسبة أسعار لعربة تسوق يمكنها استيعاب أنواع مختلفة من العناصر! ستنشئ نظامًا يمكن فيه تخزين المنتجات والخدمات، ولكل منها منطق تسعير مختلف، معًا ومعالجتها لحساب السعر الإجمالي.
ستنظّم شفرتك عبر ملفين:
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}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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 عبر الإنترنت