البرمجة

البرمجة الكائنية في Rust

البرمجة كائنية التوجه (OOP) في لغة رست Rust

تُعد البرمجة كائنية التوجه (Object-Oriented Programming – OOP) من أهم المفاهيم البرمجية في تاريخ تطوير البرمجيات، إذ مكّنت المبرمجين من بناء أنظمة برمجية معقدة وقابلة لإعادة الاستخدام والتوسع. تُعرف OOP بأنها أسلوب برمجي يركز على استخدام “الكائنات” (Objects) التي تمثل كيانات حقيقية أو منطقية، وتتكون من خصائص (Attributes) وسلوكيات (Methods). وبينما نشأت لغات برمجية تقليدية مثل Java وC++ لتطبيق هذا النمط بشكل صريح، فإن لغة رست Rust تتبع منهجًا فريدًا في دعم OOP، يمزج بين السلامة الذاكرية، الأداء العالي، والمرونة في النماذج البرمجية، مما يمنحها طابعًا عصريًا يلبي احتياجات البرمجة الحديثة.

تعريف OOP في سياق لغة Rust

في لغات OOP التقليدية، يُنظر إلى الكائنات على أنها حجر الأساس، وتُبنى التطبيقات من خلال إنشاء صنوف (Classes) ترث من أخرى وتنفذ واجهات وتغلف السلوك والبيانات. أما في Rust، فالمقاربة مختلفة، حيث لا تحتوي اللغة على صنف class بالمعنى التقليدي، لكنها تدعم مفاهيم OOP بشكل غير مباشر عبر مجموعة من الأدوات، مثل:

  • الهياكل (Structs)

  • السمات (Traits)

  • التغليف (Encapsulation)

  • التعددية الشكلية (Polymorphism)

  • التركيب بدلًا من الوراثة (Composition over Inheritance)

من خلال هذه الأدوات، يمكن تنفيذ الأنماط الكائنية بطريقة أكثر أمانًا وكفاءة من حيث استهلاك الذاكرة.

الهياكل Structs: تمثيل البيانات

في Rust، تُستخدم الهياكل (Structs) لتعريف أنواع بيانات مخصصة يمكن اعتبارها مكافئة للكائنات في لغات OOP الأخرى. يمكن أن تحتوي الهياكل على حقول (Fields) تُخزن البيانات، ومن ثم يمكن ربطها بوظائف مرتبطة باستخدام السمات Traits.

rust
struct شخص { الاسم: String, العمر: u32, }

يمثل هذا التعريف بنية “شخص” تحتوي على خصائص “الاسم” و”العمر”. لا تحتوي البنية بحد ذاتها على أي سلوك، ولكن يمكن إرفاق سلوك لها من خلال السمات.

السمات Traits: السلوك والواجهات

السمات (Traits) في Rust تشبه الواجهات (Interfaces) في Java أو C#. وهي تحدد مجموعة من الوظائف التي يمكن لأي نوع Struct أن ينفذها. ومن خلالها، يمكن تطبيق مفهوم التعددية الشكلية (Polymorphism)، أحد المبادئ الأساسية في OOP.

rust
trait التحية { fn حيِّ(&self); } impl التحية for شخص { fn حيِّ(&self) { println!("مرحبًا، اسمي {} وأنا عمري {}.", self.الاسم, self.العمر); } }

هنا، تم تعريف سمة “التحية”، وتم تنفيذها من قبل بنية “شخص”. يسمح هذا الفصل بين السلوك والبنية بتحقيق مرونة كبيرة، فضلًا عن تعزيز قابلية إعادة الاستخدام.

التغليف Encapsulation: الحماية والتحكم

رغم أن Rust لا توفر كلمات مفتاحية مثل “private” و”public” داخل الصنف، إلا أنها تتيح نفس الميكانيكية عبر التحكم في الرؤية (Visibility) باستخدام الكلمة المفتاحية pub.

rust
pub struct حساب { الرصيد: f64, } impl حساب { pub fn جديد() -> حساب { حساب { الرصيد: 0.0 } } pub fn أودع(&mut self, قيمة: f64) { self.الرصيد += قيمة; } pub fn الرصيد(&self) -> f64 { self.الرصيد } }

في المثال أعلاه، المتغير “الرصيد” غير معلن كـ pub، مما يعني أنه خاص (private) ولا يمكن الوصول إليه إلا من داخل impl. هذا هو تجسيد واضح لمفهوم التغليف، حيث يتم حماية البيانات من التعديل العشوائي ويتم التفاعل معها فقط من خلال واجهات رسمية.

الوراثة والتركيب: نماذج بديلة

بينما تعتمد لغات OOP التقليدية على الوراثة (Inheritance) لإنشاء تسلسلات صنفية، تتبنى Rust مبدأ “التركيب أفضل من الوراثة” (Composition over Inheritance). بدلاً من إنشاء تسلسل هرمي من الأصناف، يمكن بناء الكائنات عن طريق ضم خصائص وسلوكيات متعددة باستخدام السمات والتكوين البنيوي.

rust
trait قيادة { fn قد(&self); } struct سيارة { الطراز: String, } impl قيادة for سيارة { fn قد(&self) { println!("أقود سيارة {}", self.الطراز); } }

يمكن إضافة سلوكيات متعددة إلى struct واحد عبر تنفيذ عدة Traits، مما يعطي المرونة والوضوح في التصميم دون التورط في تعقيدات وراثية.

التعددية الشكلية Polymorphism

يدعم Rust التعددية الشكلية من خلال السمات. يمكن كتابة دوال تعمل على أي نوع يحقق سمة معينة، دون الحاجة إلى معرفة نوعه في وقت الترجمة.

rust
fn عرض_تحية(الكائن: &T) { الكائن.حيِّ(); }

تُعد هذه الطريقة مفيدة للغاية لبناء واجهات مرنة وقابلة للتوسع دون فقدان الأمان أو الكفاءة.

البرمجة الديناميكية (Dynamic Dispatch) مقابل الثابتة (Static Dispatch)

Rust تدعم نوعين من استدعاء الوظائف متعددة الأشكال:

  • الاستدعاء الثابت (Static Dispatch) باستخدام الصيغة T: Trait، حيث يتم تحديد نوع الكائن في وقت الترجمة، ما يؤدي إلى أداء أعلى.

  • الاستدعاء الديناميكي (Dynamic Dispatch) باستخدام Box، وهو ما يشبه استخدام المؤشرات إلى واجهات في C++ أو Java. يوفر هذا مرونة أكبر، لكنه يأتي بتكلفة أداء أعلى قليلاً.

rust
fn تنفيذ_تحية(الكائن: Box<dyn التحية>) { الكائن.حيِّ(); }

مزايا النهج الكائني في Rust

يتميز نهج OOP في Rust بعدة مزايا واضحة، من أبرزها:

الميزة التوضيح
السلامة الذاكرية بدون جامع قمامة Rust تعتمد على نظام التملك (Ownership) الذي يمنع تسرب الذاكرة دون الحاجة إلى جامع قمامة.
الأداء العالي بفضل الاستدعاء الثابت وعدم وجود تكلفة تنفيذ وقت التشغيل في معظم الحالات.
المرونة والوضوح النظام القائم على السمات يتيح بناء نماذج معمارية مرنة بدون تقييد هرمي.
التكوين بدلًا من الوراثة يشجع نمط التصميم الحديث مما يجعل الشيفرة أسهل للصيانة والاختبار.
دعم البرمجة الوظيفية مما يُسهل كتابة شيفرة أكثر إيجازًا واختبارًا.

تطبيق عملي شامل

لنفترض أننا نبني نظامًا لإدارة الموظفين في مؤسسة. نبدأ بتعريف هياكل متعددة تمثل الأنواع المختلفة للموظفين.

rust
trait موظف { fn احصل_على_الاسم(&self) -> &str; fn الراتب(&self) -> f64; } struct مهندس { الاسم: String, الراتب_الشهري: f64, } struct مدير { الاسم: String, الراتب_الشهري: f64, علاوة: f64, } impl موظف for مهندس { fn احصل_على_الاسم(&self) -> &str { &self.الاسم } fn الراتب(&self) -> f64 { self.الراتب_الشهري } } impl موظف for مدير { fn احصل_على_الاسم(&self) -> &str { &self.الاسم } fn الراتب(&self) -> f64 { self.الراتب_الشهري + self.علاوة } } fn طباعة_رواتب(الموظفين: Vec<Box<dyn موظف>>) { for م in الموظفين { println!("{} يتقاضى {:.2} دينار", م.احصل_على_الاسم(), م.الراتب()); } }

هذا المثال يبين بوضوح كيف يمكن استخدام السمات لتجريد الوظائف وتطبيق التعددية الشكلية بشكل ديناميكي وفعال.

OOP في Rust بالمقارنة مع لغات أخرى

عند مقارنة Rust بلغات مثل Java أو Python من ناحية OOP، يتضح أن Rust تعتمد على نموذج أكثر وضوحًا وكفاءة. بينما تعتمد تلك اللغات على الوراثة الكلاسيكية، يوفر نظام السمات في Rust نهجًا أكثر مرونة يسمح ببناء الكائنات عبر ضم السمات، لا عبر تسلسل هرمي معقد.

علاوة على ذلك، فإن عدم وجود جامع قمامة (Garbage Collector) يجعل Rust أكثر كفاءة من حيث إدارة الموارد، وهو أمر بالغ الأهمية في الأنظمة ذات الموارد المحدودة مثل برمجيات الأنظمة والتطبيقات المضمنة.

خلاصة الأسلوب الكائني في Rust

بالرغم من أن Rust لا تُصنف كلغة كائنية بالكامل، فإنها تقدم بيئة تدعم المبادئ الأساسية للبرمجة كائنية التوجه بطرق حديثة وفعالة وآمنة. باستخدام الهياكل والسمات والتركيب، يمكن بناء تطبيقات برمجية تعتمد على الكائنات وتستفيد من إمكانيات النظام القوي للملكية والسلامة الذاكرية.

المراجع

  1. The Rust Programming Language – OOP Chapter

  2. Rust by Example – Traits