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

صيغة قيود Traits

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

حتى الآن، تعلمت كيفية تعريف السمات وتنفيذها للهياكل. كما عملت مع الأنواع العامة لكتابة شيفرة مرنة. حان الوقت الآن لدمج هذه المفاهيم: استخدام السمات لتقييد أنواع البيانات التي يمكن لدالة generic acceptها.

تخيّل generic function تحتاج إلى call method محدد على معاملها. بدون أي قيود، لا يضمن Rust أن النوع T لديه ذلك method:

fn print_info<T>(item: T) {
    println!("{}", item.summarize());  // خطأ! قد لا يمتلك T الدالة summarize()
}

الحل هو قيد السمة. بإضافة : TraitName بعد المعامل العام، تخبر Rust بأن T يجب أن يطبّق تلك السمة:

trait Summary {
    fn summarize(&self) -> String;
}

fn print_info<T: Summary>(item: T) {
    println!("{}", item.summarize());  // الآن يعرف Rust أن هذه الـ method موجودة
}

تُقرأ الصياغة <T: Summary> على أنها «‏T هو أي نوع يطبّق Summary». والآن لا تقبل الدالة إلا الأنواع التي have السلوك المطلوب. إذا حاولت تمرير نوع لا يطبّق Summary، فسيرفضه المصرّف مع ظهور خطأ واضح.

هذا النمط قوي لأنه يتيح لك كتابة تعليمات برمجية generic مع إمكانية الوصول إلى وظائف محددة. تظل function الخاصة بك مرنة، فهي تعمل مع any نوع، ولكن فقط مع الأنواع التي توفر السلوك الذي تحتاج إليه.

challenge icon

التحدي

سهل

لنَبْنِ نظام إشعارات يستخدم حدود السمات لضمان إمكانية إرسال الرسائل المنسّقة بشكل صحيح فقط! ستنشئ generic function تقبل أي نوع يطبّق سمة محددة، موضحًا كيفية تقييد المعاملات العامة باستخدام حدود السمات.

ستنظّم التعليمات البرمجية الخاصة بك عبر ملفين:

  • notification.rs: عرّف سمة Notify من النوع public مع method باسم message يأخذ &self ويعيد قيمة من النوع String. ثم أنشئ بنيتين من النوع public تطبّقان هذه السمة:
    • Email: مع field باسم subject من النوع public (String). يجب أن تعيد method الخاصة بها message القيمة Email: {subject}
    • SMS: مع field باسم content من النوع public (String). يجب أن تعيد method الخاصة بها message القيمة SMS: {content}
    وأخيرًا، أنشئ generic function من النوع public باسم send_notification تقبل أي نوع T يطبّق السمة Notify. يجب أن تطبع هذه function result استدعاء message() على العنصر.
  • main.rs: أضف وحدة الإشعارات الخاصة بك واستخدم inputs المقدّمة لإنشاء كل من instance من النوع Email وinstance من النوع SMS. استدعِ send_notification مع كل منهما لإظهار أن generic function الخاصة بك تعمل مع أي نوع يطبّق Notify.

الفكرة الأساسية هنا هي أن send_notification لا تعرف النوع الفعلي الذي تستقبله. إنها تعرف فقط أن النوع قادر على إنتاج message. ويضمن حد السمة T: Notify هذه القدرة.

يجب أن يُظهر الناتج إرسال الإشعارين معًا:

Email: {subject}
SMS: {content}

على سبيل المثال، مع inputs Meeting Tomorrow وOn my way!:

Email: Meeting Tomorrow
SMS: On my way!

ستتلقى inputين: موضوع البريد الإلكتروني ومحتوى SMS.

جرّب بنفسك

mod notification;

use notification::{Email, SMS, send_notification};

fn main() {
    // قراءة المدخلات
    let mut subject = String::new();
    std::io::stdin().read_line(&mut subject).expect("Failed to read line");
    let subject = subject.trim().to_string();
    
    let mut content = String::new();
    std::io::stdin().read_line(&mut content).expect("Failed to read line");
    let content = content.trim().to_string();
    
    // TODO: أنشئ مثيل Email باستخدام subject
    
    // TODO: أنشئ مثيل SMS باستخدام content
    
    // TODO: استدعِ send_notification مع email
    
    // TODO: استدعِ send_notification مع sms
}
quiz iconاختبر نفسك

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

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

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