الإلغاء التلقائي للمراجع في Rust: كيف يعمل ولماذا ليس سحرًا
\n\nإذا كنت قد كتبت كودًا بلغة Rust من قبل، فبالتأكيد لاحظت أن المترجم يسمح باستدعاء الدوال على المراجع والمؤشرات الذكية كما لو كنت تتعامل مع القيمة نفسها. هذا ليس سحرًا ولا خطأ - بل هو آلية محددة بدقة تسمى الإلغاء التلقائي للمراجع (auto-dereferencing).
\n\nعلى Stack Overflow، جمع سؤال «What are Rust's exact auto-dereferencing rules?» آلاف المشاهدات - وليس بدون سبب. فهم هذه القواعد يحول الكود في Rust من «مجموعة من الأخطاء الغريبة» إلى أداة أنيقة ويمكن التنبؤ بها. دعونا نلقي نظرة تحت الغطاء.
\n\nما هو الإلغاء التلقائي للمراجع ولماذا هو مطلوب؟
\n\nفي Rust، لدينا مراجع (&T, &mut T) ومؤشرات ذكية (Box<T>, Rc<T>, Arc<T>). عندما تكتب my_box.some_method()، يقوم المترجم تلقائيًا بتطبيق الإلغاء للوصول إلى القيمة الداخلية T. بدون هذه الآلية، سيتعين عليك كتابة (*my_box).some_method() أو (**my_ref).some_method() - مما يصبح سريعًا مملًا ويشوش الكود.
الإلغاء التلقائي للمراجع يجعل الكود نظيفًا، ولكن من المهم فهم حدوده وقواعده الدقيقة لتجنب الوقوع في الفخ.
\n\nالقواعد الدقيقة للإلغاء التلقائي للمراجع في Rust
\n\nدعونا نحلل المواصفات الرسمية. عندما يواجه المترجم تعبيرًا من الشكل x.method()، يحاول العثور على دالة باسم method للنوع T، حيث T هو نوع x. إذا لم يتم العثور على الدالة، يبدأ المترجم عملية الإلغاء التلقائي (أو أخذ المرجع).
إليك تسلسل الخطوات التي ينفذها المترجم (بشكل مبسط):
\n\n- \n
- الخطوة 1: محاولة العثور على دالة للنوع
T(كما هو). \n - الخطوة 2: إذا لم يتم العثور عليها - محاولة العثور على دالة لـ
&Tأو&mut T(أخذ المرجع). \n - الخطوة 3: إذا لم يتم العثور عليها - تطبيق
Deref، أي الإلغاء مرة واحدة (الحصول علىUمنT). \n - الخطوة 4: تكرار الخطوات 1-3 للنوع الجديد
U. \n - الخطوة 5: إذا لم يتم العثور على الدالة بعد عدة عمليات إلغاء - خطأ في الترجمة. \n
هام: المترجم لا يصنع سلسلة لا نهائية. يطبق الإلغاء حتى يعثر على الدالة أو يصل إلى نوع لا يطبق Deref.
أمثلة: من البسيط إلى المعقد
\n\nدعونا نلقي نظرة على كود يوضح هذه القواعد عمليًا.
\n\nمثال 1: مرجع أساسي
\n\nstruct Point {\n x: i32,\n y: i32,\n}\n\nimpl Point {\n fn distance_from_origin(&self) -> f64 {\n ((self.x.pow(2) + self.y.pow(2)) as f64).sqrt()\n }\n}\n\nfn main() {\n let p = Point { x: 3, y: 4 };\n let r = &p; // &Point\n println!("{}", r.distance_from_origin()); // 5.0\n}\n\nهنا r هو &Point. الدالة distance_from_origin تأخذ &self. يرى المترجم: r هو &Point. يحاول العثور على دالة لـ &Point - يجدها! كل شيء يعمل دون إلغاء صريح.
مثال 2: مرجع مزدوج
\n\nfn main() {\n let p = Point { x: 3, y: 4 };\n let r1 = &p; // &Point\n let r2 = &r1; // &&Point\n println!("{}", r2.distance_from_origin()); // 5.0\n}\n\nنوع r2 هو &&Point. يحاول المترجم العثور على دالة لـ &&Point - لا توجد. يحاول أخذ مرجع: &&&Point - أيضًا لا توجد. ثم يطبق Deref مرة واحدة: يحصل على &Point. بالنسبة لـ
المدونات
توصيات الكتب
اقرأ أيضًا