الهياكل المرجعية الذاتية في Rust: دليل تعليمي خطوة بخطوة

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

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

ابدأ الدورة

لماذا لا تسمح لك Rust بتخزين مرجع إلى نفسها: تحليل بالأمثلة

\n\n

تخيل موقفًا: أنت تكتب برنامجًا بلغة Rust، وتحتاج إلى إنشاء هيكل يخزن بعض البيانات، بالإضافة إلى مرجع إلى نفس هذه البيانات. منطقي؟ بالتأكيد! لكن مترجم Rust فجأة يظهر خطأ، وأنت في حيرة. هل هذا مألوف؟ هذا السؤال هو من أكثر الأسئلة شيوعًا على Stack Overflow (آلاف المشاهدات)، واليوم سنقوم بتحليله بالكامل.

\n\n

في هذا الدليل التعليمي خطوة بخطوة، لن نفهم فقط لماذا لا يمكن تخزين قيمة ومرجع في نفس الهيكل في Rust، بل سنتعلم أيضًا كيفية تجاوز هذا القيد بطرق آمنة. هيا بنا!

\n\n

المشكلة: محاولة ساذجة

\n\n

لنفترض أننا نريد إنشاء هيكل يخزن سلسلة نصية وحرفها الأول (مرجع إلى الحرف الأول). إليك كيف يبدو ذلك في الكود:

\n\n
struct MyStruct {\n    data: String,\n    first_char: &str,  // مرجع إلى data\n}\n\nfn main() {\n    let s = String::from("Hello");\n    let first = &s[0..1];  // مرجع إلى الحرف الأول\n    \n    let my_struct = MyStruct {\n        data: s,\n        first_char: first,  // نحاول حفظ المرجع\n    };\n}\n
\n\n

يظهر المترجم خطأ يحير العديد من المبتدئين. دعنا نفهم ما حدث بالضبط.

\n\n

لماذا تمنع Rust الهياكل المرجعية الذاتية؟

\n\n

جذر المشكلة يكمن في نظام الملكية (ownership) والاقتراض (borrowing) في Rust. عندما تنقل (move) القيمة s إلى الحقل data، فإنها تُنسخ فعليًا إلى موقع جديد في الذاكرة. المرجع first في هذه الأثناء لا يزال يشير إلى العنوان القديم، الذي أصبح الآن غير صالح.

\n\n

في Rust، يجب أن يكون كل المرجع صالحًا طوال فترة وجوده. الهياكل المرجعية الذاتية في Rust تخالف هذه القاعدة، لأن المرجع والقيمة يعيشان في نفس الكائن، وعند نقل الهيكل، يصبح المرجع معلقًا (dangling reference).

\n\n

ماذا يحدث تحت الغطاء؟

\n\n

عندما تكتب هذا الكود:

\n\n
let s = String::from("Hello");\nlet first = &s[0..1];\nlet my_struct = MyStruct { data: s, first_char: first };\n
\n\n

يحدث التالي:

\n
    \n
  1. s يملك السلسلة "Hello" في الكومة.
  2. \n
  3. first هو مرجع لجزء من هذه السلسلة.
  4. \n
  5. عند إنشاء الهيكل data: s، تنتقل ملكية السلسلة إلى الهيكل. قد تنتقل السلسلة في الذاكرة.
  6. \n
  7. المرجع first لا يزال يشير إلى العنوان القديم - هذا مرجع معلق. لا يمكن لـ Rust السماح بذلك.
  8. \n
\n\n

Borrow checker في Rust يرى هذا الخطر المحتمل ويمنع الكود في مرحلة التجميع. هذا ليس خطأ، بل ميزة تحميك من العديد من المشاكل في وقت التشغيل.

\n\n

سيناريوهات حقيقية تحتاج فيها إلى هياكل مرجعية ذاتية

\n\n

على الرغم من المنع، قد تكون هذه الهياكل مفيدة. إليك حالات نموذجية:

\n\n
    \n
  • المحللات (Parsers) وشجرة النحو المجرد (AST): عندما تشير عقد الشجرة إلى بعضها البعض.
  • \n
  • محركات الألعاب: كيانات تحتوي على مراجع لمكوناتها.
  • \n
  • بروتوكولات الشبكات: مخازن مؤقتة بمؤشرات داخلية.
  • \n
  • أنظمة الإضافات: عندما يحتوي كائن على مرجع إلى إعداداته.
  • \n
\n\n

الخبر الجيد: هناك طرق آمنة لتجاوز هذا القيد!

\n\n

الحل 1: استخدام الفهارس بدلاً من المراجع (أبسط طريقة)

\n\n

أبسط وأكثر طريقة آمنة هي تخزين فهرس أو مفتاح بدلاً من المرجع. بدلاً من &str، قم بتخزين usize (موضع الحرف الأول).

\n\n
struct MyStruct {\n    data: String,\n    first_char_index: usize,  // نخزن الموضع، وليس المرجع\n}\n\nimpl MyStruct {\n    fn first_char(&self) -> &                

المدونات

توصيات الكتب