صيغة قيود 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، فسيقوم المترجم برفضه مع ظهور خطأ واضح.
هذا النمط قوي لأنه يتيح لك كتابة كود عام لا يزال بإمكانه الوصول إلى وظائف محددة. تظل الدالة الخاصة بك مرنة — فهي تعمل مع أي نوع — ولكن فقط الأنواع التي توفر السلوك الذي تحتاجه.
التحدي
سهللنقم ببناء نظام إشعارات يستخدم قيود السمات (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 قم باستدعاء
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1الأساليب والسلوك
مقدمة في كتل التنفيذمعامل Selfالأساليب القابلة للتعديلالدوال المرتبطةكتل تنفيذ متعددةتسلسل الأساليبمراجعة - إجراءات المستطيل4مشروع: حيوان أليف افتراضي
تعريف الحيوان الأليفإطعام الحيوان الأليف2التغليف والوحدات البرمجية
أساسيات الوحدات البرمجيةالكلمة المفتاحية Publicالحقول الخاصةدوال Gettersدوال Settersمراجعة - الخزانة الآمنة5الأنواع العامة (Generics)
الـ Structs العامةالـ Methods العامةأنواع عامة متعددةالـ Functions العامةمراجعة - Coordinate Point8Traits كقيود
صيغة قيود Traitsقيود متعددةجملة Whereإرجاع الأنواع باستخدام Traitsمراجعة - طابعة Generic3التعدادات المتقدمة (Advanced Enums)
التعدادات مع البياناتالأساليب (Methods) في التعداداتمطابقة حالات البياناتمراجعة التعداد Optionملخص - التعداد Shape6تعريف الـ Traits
ما هي الـ Trait؟تنفيذ الـ Traitsالتنفيذات الافتراضيةتجاوز التنفيذات الافتراضيةالـ Traits مع المعاملاتملخص - مشغل الوسائط