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

صيغة قيود Traits

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

حتى الآن، تعلمت كيفية تعريف السمات (traits) وتنفيذها للهياكل (structs). لقد عملت أيضًا مع الأنواع العامة (generics) لكتابة كود مرن. الآن حان الوقت للجمع بين هذه المفاهيم—باستخدام السمات (traits) لـ تقييد أنواع البيانات التي يمكن للدالة العامة (generic function) قبولها.

فكر في دالة عامة (generic function) تحتاج إلى استدعاء تابع (method) محدد على معاملها. بدون أي قيود، ليس لدى Rust أي ضمان بأن النوع T يمتلك ذلك التابع:

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

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

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

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

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

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

challenge icon

التحدي

سهل

لنقم ببناء نظام إشعارات يستخدم قيود السمات (trait bounds) لضمان إرسال الرسائل المنسقة بشكل صحيح فقط! ستقوم بإنشاء دالة عامة (generic function) تقبل أي نوع يطبق سمة معينة، مما يوضح كيف تقيد قيود السمات المعاملات العامة.

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

  • notification.rs: قم بتعريف سمة عامة (public trait) تسمى Notify مع طريقة تسمى message تأخذ &self وتعيد String. ثم قم بإنشاء هيكلين عامين (public structs) يطبقان هذه السمة:
    • Email — مع حقل عام subject من نوع (String). يجب أن تعيد طريقة message الخاصة به Email: {subject}
    • SMS — مع حقل عام content من نوع (String). يجب أن تعيد طريقة message الخاصة به SMS: {content}
    أخيرًا، قم بإنشاء دالة عامة عامة تسمى send_notification تقبل أي نوع T يطبق سمة Notify. يجب أن تطبع هذه الدالة نتيجة استدعاء message() على العنصر.
  • main.rs: قم باستيراد وحدة الإشعارات الخاصة بك واستخدم المدخلات المقدمة لإنشاء مثيل لكل من Email و SMS. قم باستدعاء send_notification مع كل منهما لإثبات أن دالتك العامة تعمل مع أي نوع يطبق Notify.

الفكرة الأساسية هنا هي أن send_notification لا تعرف النوع الملموس (concrete type) الذي تستقبله — فهي تعرف فقط أن النوع يمكنه إنتاج رسالة. يضمن قيد السمة T: Notify هذه القدرة.

يجب أن يظهر مخرجك إرسال كلا الإشعارين:

Email: {subject}
SMS: {content}

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

Email: Meeting Tomorrow
SMS: On my way!

ستتلقى مدخلين: موضوع البريد الإلكتروني ومحتوى الرسالة القصيرة (SMS).

ورقة مرجعية

تقيد قيود السمات (trait bound) معامل النوع العام ليقبل فقط الأنواع التي تُنفذ سمة (trait) معينة.

تستخدم صيغة قيود السمات : TraitName بعد المعامل العام:

fn function_name<T: TraitName>(item: T) {
    // يمكن الآن استدعاء دوال من TraitName على item
}

مثال على قيد السمة:

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

fn print_info<T: Summary>(item: T) {
    println!("{}", item.summarize());
}

التدوين <T: Summary> يعني أن "T هو أي نوع يُنفذ Summary". يسمح هذا للدالة باستدعاء دوال السمة مع بقائها عامة (generic).

جرّب بنفسك

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 قم بإنشاء مثيل من
    
    // TODO: مع محتوى الرسالة SMS قم بإنشاء مثيل من
    
    // TODO: مع البريد الإلكتروني send_notification قم باستدعاء
    
    // TODO: مع الرسالة النصية send_notification قم باستدعاء
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming