self مقابل Self في Rust: دليل شامل للمبتدئين وما بعد

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: حرف واحد صغير، فرق كبير

\n\n

مرحبًا، أيها المطور المستقبلي بلغة Rust! إذا كنت قد بدأت بالفعل في تعلم Rust، فمن المحتمل أنك لاحظت أن المترجم (compiler) هو معلم صارم ولكن عادل. إنه لا يتسامح مع الأخطاء المطبعية، ناهيك عن الخلط في بناء الجملة. أحد أكثر الفخاخ شيوعًا التي يقع فيها المبتدئون (وليس فقط) هو الخلط بين self و Self. قد يبدو الفرق مجرد حرف واحد - كبير أو صغير. لكن خلف هذا الحرف يكمن فرق جوهري بين المثيل (instance) و النوع (type).

\n\n

على Stack Overflow، حصد هذا السؤال آلاف المشاهدات، وهذا ليس من قبيل الصدفة. اليوم سنفهم مرة واحدة وإلى الأبد الفرق بين self و Self في Rust. لن نقرأ مجرد تعريف ممل، بل سنخوض برنامجًا تعليميًا خطوة بخطوة مع أمثلة حية، ونكتب بضعة برامج صغيرة. بحلول نهاية المقال، لن تعرف الفرق فحسب، بل ستشعر به على مستوى بديهي. هيا بنا!

\n\n

1. self: "أنا" - هذا يتعلق بالمثيل

\n\n

لنبدأ بـ self. في Rust، كما في العديد من اللغات الأخرى، يُستخدم self للإشارة إلى المثيل الحالي للهيكل (struct) أو التعداد (enum). إنه مشابه لـ this في C++ و Java و JavaScript أو Python. عندما تكتب دالة (method) يتم استدعاؤها على كائن معين، فإنك تستخدم self.

\n\n

لـ self ثلاثة أشكال رئيسية في توقيع الدالة:

\n
    \n
  • self - يأخذ ملكية المثيل (consumes the instance). بعد استدعاء الدالة، يصبح الكائن الأصلي غير متاح.
  • \n
  • &self - مرجع غير قابل للتغيير إلى المثيل (الحالة الأكثر شيوعًا). يمكن للدالة قراءة البيانات ولكن لا يمكنها تغييرها.
  • \n
  • &mut self - مرجع قابل للتغيير إلى المثيل. يمكن للدالة تغيير حقول الهيكل.
  • \n
\n\n

لنكتب مثالًا بسيطًا. تخيل أننا ننشئ هيكلًا باسم طالب.

\n\n
struct طالب {\n    الاسم: String,\n    السنة: u32,\n}\n\nimpl طالب {\n    // دالة تأخذ &self (مرجع غير قابل للتغيير)\n    fn قدم_نفسك(&self) {\n        println!("مرحبًا! أنا {}، أدرس في السنة {}.", self.الاسم, self.السنة);\n    }\n\n    // دالة تأخذ &mut self (مرجع قابل للتغيير)\n    fn انتقل_إلى_السنة_التالية(&mut self) {\n        self.السنة += 1;\n        println!("{} الآن في السنة {}!", self.الاسم, self.السنة);\n    }\n\n    // دالة تأخذ self (الملكية)\n    fn أنهِ_الدراسة(self) {\n        println!("{} أنهى الدراسة في السنة {}. وداعًا!", self.الاسم, self.السنة);\n        // يتم تدمير self هنا، ويتم تحرير الذاكرة\n    }\n}\n\nfn main() {\n    let mut طالب_مثال = طالب {\n        الاسم: String::from("آنا"),\n        السنة: 2,\n    };\n\n    طالب_مثال.قدم_نفسك(); // &self\n    طالب_مثال.انتقل_إلى_السنة_التالية(); // &mut self\n    طالب_مثال.أنهِ_الدراسة(); // self (الملكية)\n\n    // بعد استدعاء أنهِ_الدراسة، المتغير 'طالب_مثال' لم يعد متاحًا!\n    // قم بإلغاء تعليق السطر التالي لرؤية خطأ الترجمة:\n    // طالب_مثال.قدم_نفسك(); // خطأ: borrow of moved value\n}
\n\n

لاحظ التعليق الأخير. عندما نمرر self (بدون مرجع)، فإننا نتخلى عن الملكية. هذه أداة قوية تسمح بالتحكم الدقيق في عمر الكائن. في اللغات الأخرى (مثل Python)، لا يوجد مثل هذا التحكم - الكائن يعيش طالما توجد مراجع إليه. في Rust، أنت من يقرر متى سيتم "استهلاك" الكائن.

\n\n

2. Self: "أنا" - هذا يتعلق بالنوع

\n\n

والآن الجزء الأكثر إثارة للاهتمام. Self (بحرف كبير) هو اسم مستعار (alias) للنوع الذي نقوم بتنفيذ الدوال له. ببساطة، Self داخل كتلة impl يتم استبداله باسم الهيكل أو التعداد الذي نعمل معه.

\n\n

لماذا نحتاج هذا؟ أولاً، إنه مناسب. بدلاً من كتابة الاسم الكامل للهيكل، تكتب Self. ثانيًا، إنه أمر بالغ الأهمية

المدونات

توصيات الكتب