الإلغاء التلقائي للمراجع في Rust: القواعد والأمثلة و"السحر" تحت الغطاء

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

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

ابدأ الدورة

الإلغاء التلقائي للمراجع في 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

الإلغاء التلقائي للمراجع يجعل الكود نظيفًا، ولكن من المهم فهم حدوده وقواعده الدقيقة لتجنب الوقوع في الفخ.

\n\n

القواعد الدقيقة للإلغاء التلقائي للمراجع في Rust

\n\n

دعونا نحلل المواصفات الرسمية. عندما يواجه المترجم تعبيرًا من الشكل x.method()، يحاول العثور على دالة باسم method للنوع T، حيث T هو نوع x. إذا لم يتم العثور على الدالة، يبدأ المترجم عملية الإلغاء التلقائي (أو أخذ المرجع).

\n\n

إليك تسلسل الخطوات التي ينفذها المترجم (بشكل مبسط):

\n\n
    \n
  1. الخطوة 1: محاولة العثور على دالة للنوع T (كما هو).
  2. \n
  3. الخطوة 2: إذا لم يتم العثور عليها - محاولة العثور على دالة لـ &T أو &mut T (أخذ المرجع).
  4. \n
  5. الخطوة 3: إذا لم يتم العثور عليها - تطبيق Deref، أي الإلغاء مرة واحدة (الحصول على U من T).
  6. \n
  7. الخطوة 4: تكرار الخطوات 1-3 للنوع الجديد U.
  8. \n
  9. الخطوة 5: إذا لم يتم العثور على الدالة بعد عدة عمليات إلغاء - خطأ في الترجمة.
  10. \n
\n\n

هام: المترجم لا يصنع سلسلة لا نهائية. يطبق الإلغاء حتى يعثر على الدالة أو يصل إلى نوع لا يطبق Deref.

\n\n

أمثلة: من البسيط إلى المعقد

\n\n

دعونا نلقي نظرة على كود يوضح هذه القواعد عمليًا.

\n\n

مثال 1: مرجع أساسي

\n\n
struct 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 - يجدها! كل شيء يعمل دون إلغاء صريح.

\n\n

مثال 2: مرجع مزدوج

\n\n
fn 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. بالنسبة لـ

المدونات

توصيات الكتب