مقدمة: عالمين في لغة واحدة
\n\nعندما تبدأ في تعلم Rust، ستلاحظ بسرعة موقعها الفريد. هذه اللغة لا تفرض عليك طريقة واحدة في التفكير. على عكس Java (حيث كل شيء هو كائن) أو Haskell (حيث كل شيء هو دالة)، تقدم لك Rust حرية الاختيار. ولكن مع هذه الحرية تأتي المسؤولية: أي أسلوب تختار - الوظيفي أم الكائني التوجه؟
\n\nالعديد من المطورين القادمين إلى Rust من عالم Python أو C++، ينجذبون بشكل حدسي نحو OOP (البرمجة الكائنية التوجه). إنهم يبحثون عن الفئات، الوراثة والأنماط المألوفة. آخرون، مستلهمين من عالم Haskell أو Scala، يحاولون البرمجة بلغة Rust حصريًا بالأسلوب الوظيفي، باستخدام المونادات، الثبات والتركيب.
\n\nالحقيقة، كما هو الحال دائمًا، تقع في المنتصف. Rust هي لغة متعددة النماذج تسمح لك بأخذ أفضل ما في العالمين. في هذه المقالة، سنحلل كيفية استخدام الأسلوب الوظيفي في Rust بفعالية ومتى يجب تطبيق الأنماط الكائنية التوجه. ستتعلم كيفية كتابة كود لا يعمل فقط، بل يسعد بأناقته أيضًا.
\n\nأساسيات OOP في Rust: السمات بدلاً من الفئات
\n\nدعنا نضع النقاط على الحروف فورًا. لا توجد فئات في Rust بالمعنى التقليدي. لكن هذا لا يعني أنه لا يمكنك استخدام OOP. بدلاً من الفئات، تُستخدم هنا السمات (traits) والهياكل (structs). هي التي تسمح بتحقيق التغليف، تعدد الأشكال وحتى بعض الشبه بالوراثة.
\n\nالهياكل والطرق
\n\nالهياكل هي "الكائنات" الخاصة بك. تقوم بتعريف البيانات، ثم باستخدام impl تضيف إليها الطرق:
struct Player {\n name: String,\n health: i32,\n level: u32,\n}\n\nimpl Player {\n // طريقة المُنشئ\n fn new(name: &str) -> Self {\n Player {\n name: name.to_string(),\n health: 100,\n level: 1,\n }\n }\n\n // طريقة تغير الحالة\n fn take_damage(&mut self, damage: i32) {\n self.health = (self.health - damage).max(0);\n }\n\n // دالة الوصول (getter)\n fn is_alive(&self) -> bool {\n self.health > 0\n }\n}\n\n\nهذا هو أسلوب OOP الخالص: لدينا حالة (حقول الهيكل) وسلوك (طرق). توفر Rust التغليف من خلال التحكم في الرؤية (معدل pub).
تعدد الأشكال عبر السمات
\n\nبدلاً من وراثة الفئات، يُستخدم في Rust تعدد الأشكال عبر السمات. هذا أقوى وأكثر أمانًا من الوراثة التقليدية، حيث لا تواجه "مشكلة الماس" أو وراثة سلوك غير مرغوب فيه عن طريق الصدفة.
\n\ntrait Attackable {\n fn attack(&self, target: &mut Player);\n}\n\nstruct Swordsman {\n damage: i32,\n}\n\nimpl Attackable for Swordsman {\n fn attack(&self, target: &mut Player) {\n target.take_damage(self.damage);\n println!("ألحق المبارز {} ضررًا!", self.damage);\n }\n}\n\nstruct Mage {\n spell_power: i32,\n mana_cost: i32,\n}\n\nimpl Attackable for Mage {\n fn attack(&self, target: &mut Player) {\n target.take_damage(self.spell_power * 2);\n println!("أطلق الساحر كرة نارية بقوة {}!", self.spell_power * 2);\n }\n}\n\n\nالآن يمكنك العمل مع أي كائن يطبق السمة Attackable، دون القلق بشأن نوعه المحدد:
fn perform_attack(attacker: &impl Attackable, target: &mut Player) {\n attacker.attack(target);\n}\n\n\nيُسمى هذا "الكتابة البطية على المنشطات" أو تعدد الأشكال الثابت. تتحقق Rust من كل شيء في مرحلة الترجمة، لذلك لن تحصل على خطأ في وقت التشغيل.
\n\nالأسلوب الوظيفي: المكررات والإغلاقات
\n\nالآن ننتقل