الـ Traits مع المعاملات
جزء من قسم Object Oriented Programming في رحلة Rust على Coddy — الدرس 33 من 61.
حتى الآن، لم تأخذ طرق السمات (trait methods) الخاصة بنا سوى &self كمعلمة. ولكن يمكن لطرق السمات قبول معلمات إضافية تماماً مثل الطرق العادية—وهذا يجعلها أكثر فائدة بكثير للعمليات الواقعية.
عند تعريف سمة (trait)، يمكنك تضمين أي معلمات تحتاجها في تواقيع الدوال (method signatures):
trait Calculator {
fn add(&self, a: i32, b: i32) -> i32;
fn multiply(&self, a: i32, b: i32) -> i32;
}
تتطلب سمة Calculator هذه طريقتين، كل منهما تقبل عددين صحيحين بالإضافة إلى &self. يجب على أي نوع يقوم بتنفيذ هذه السمة توفير كلتا العمليتين بهذه التواقيع المحددة تماماً.
إليك كيف يمكن لـ struct تنفيذ ذلك:
struct BasicCalc;
impl Calculator for BasicCalc {
fn add(&self, a: i32, b: i32) -> i32 {
a + b
}
fn multiply(&self, a: i32, b: i32) -> i32 {
a * b
}
}
الآن يمكنك استخدام هذه الطرق مع الوسائط:
let calc = BasicCalc;
println!("{}", calc.add(5, 3)); // 8
println!("{}", calc.multiply(4, 7)); // 28
تحدد السمة (trait) ما هي المعاملات التي تقبلها كل دالة (method)، بينما يحدد التنفيذ (implementation) كيفية استخدام تلك المعاملات. يتيح ذلك للأنواع المختلفة تنفيذ العمليات نفسها بطريقتها الخاصة—فربما يقوم LoggingCalc بطباعة كل عملية قبل إرجاع النتيجة.
التحدي
سهللنقم ببناء مجموعة أدوات لمعالجة النصوص حيث يمكن للمعالجات المختلفة تحويل النص بطرقها الخاصة! ستقوم بإنشاء سمة (trait) باسم TextProcessor تحتوي على دوال تقبل معاملات، ثم تقوم بتنفيذها لنوعين مختلفين من المعالجات.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
processor.rs: قم بتعريف سمة عامةTextProcessorمع دالتين تقبلان معاملات:repeat(&self, text: &str, times: u32) -> String— تكرر النص المعطى عدداً محدداً من المرات.truncate(&self, text: &str, max_len: usize) -> String— تقصر النص إلى الحد الأقصى المحدد للطول.
processors.rs: قم بإنشاء هيكلين (structs) عامين ينفذان السمة الخاصة بك:SimpleProcessor— هيكل وحدة (unit struct) يكرر النص مع وجود مسافات بين كل تكرار (على سبيل المثال، "Hi" مكررة 3 مرات تصبح "Hi Hi Hi") ويقوم بالقص ببساطة عند الحد الأقصى للطول.FancyProcessor— هيكل وحدة يكرر النص مع وجود " * " بين التكرارات (على سبيل المثال، "Hi" مكررة 3 مرات تصبح "Hi * Hi * Hi") ويقوم بالقص عن طريق إضافة "..." إذا تم تقصير النص (فقط إذا كان النص الأصلي أطول من max_len).
main.rs: اجمع وحداتك معاً واعرض كيف تنتج نفس دوال السمة نتائج مختلفة بناءً على المعالج الذي تستخدمه.
في ملفك الرئيسي، قم بإنشاء كلا المعالجين واستخدم المدخلات لاختبارهما. ستتلقى ثلاثة مدخلات: سلسلة نصية، وعدد التكرارات، والحد الأقصى للطول.
اطبع أربعة أسطر توضح سلوك كل معالج:
Simple repeat: {result}
Simple truncate: {result}
Fancy repeat: {result}
Fancy truncate: {result}على سبيل المثال، مع المدخلات Hello، و 3، و 4:
Simple repeat: Hello Hello Hello
Simple truncate: Hell
Fancy repeat: Hello * Hello * Hello
Fancy truncate: Hell...لاحظ كيف يفي كلا المعالجين بنفس عقد TextProcessor، ولكن كل منهما يحول النص بطريقته الفريدة. تحدد السمة (trait) ما هي المعاملات التي تقبلها الدوال، بينما يقرر كل تنفيذ كيفية استخدامها!
ستتلقى ثلاثة مدخلات: النص المراد معالجته، وعدد التكرارات (يتم تحليله كـ u32)، والحد الأقصى للطول للقص (يتم تحليله كـ usize).
ورقة مرجعية
يمكن لأساليب السمة (Trait methods) قبول معلمات إضافية تتجاوز &self:
trait Calculator {
fn add(&self, a: i32, b: i32) -> i32;
fn multiply(&self, a: i32, b: i32) -> i32;
}
تنفيذ سمة (trait) بمعلمات:
struct BasicCalc;
impl Calculator for BasicCalc {
fn add(&self, a: i32, b: i32) -> i32 {
a + b
}
fn multiply(&self, a: i32, b: i32) -> i32 {
a * b
}
}
استخدام أساليب السمة مع الوسائط (arguments):
let calc = BasicCalc;
println!("{}", calc.add(5, 3)); // 8
println!("{}", calc.multiply(4, 7)); // 28
تحدد السمة (trait) ما هي المعلمات التي يقبلها كل أسلوب، بينما يحدد التنفيذ كيفية استخدام تلك المعلمات.
جرّب بنفسك
mod processor;
mod processors;
use processor::TextProcessor;
use processors::{SimpleProcessor, FancyProcessor};
fn main() {
// قراءة المدخلات
let mut text = String::new();
std::io::stdin().read_line(&mut text).expect("Failed to read line");
let text = text.trim();
let mut times_input = String::new();
std::io::stdin().read_line(&mut times_input).expect("Failed to read line");
let times: u32 = times_input.trim().parse().expect("Failed to parse times");
let mut max_len_input = String::new();
std::io::stdin().read_line(&mut max_len_input).expect("Failed to read line");
let max_len: usize = max_len_input.trim().parse().expect("Failed to parse max_len");
// إنشاء المعالجات
let simple = SimpleProcessor;
let fancy = FancyProcessor;
// TODO: استخدم المعالجات لتحويل النص وطباعة النتائج
// طباعة أربعة أسطر:
// Simple repeat: {result}
// Simple truncate: {result}
// Fancy repeat: {result}
// Fancy truncate: {result}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1الأساليب والسلوك
مقدمة في كتل التنفيذمعامل Selfالأساليب القابلة للتعديلالدوال المرتبطةكتل تنفيذ متعددةتسلسل الأساليبمراجعة - إجراءات المستطيل4مشروع: حيوان أليف افتراضي
تعريف الحيوان الأليفإطعام الحيوان الأليف2التغليف والوحدات البرمجية
أساسيات الوحدات البرمجيةالكلمة المفتاحية Publicالحقول الخاصةدوال Gettersدوال Settersمراجعة - الخزانة الآمنة5الأنواع العامة (Generics)
الـ Structs العامةالـ Methods العامةأنواع عامة متعددةالـ Functions العامةمراجعة - Coordinate Point3التعدادات المتقدمة (Advanced Enums)
التعدادات مع البياناتالأساليب (Methods) في التعداداتمطابقة حالات البياناتمراجعة التعداد Optionملخص - التعداد Shape6تعريف الـ Traits
ما هي الـ Trait؟تنفيذ الـ Traitsالتنفيذات الافتراضيةتجاوز التنفيذات الافتراضيةالـ Traits مع المعاملاتملخص - مشغل الوسائط