الطفرة في Rust: قبل النقطتين أم بعدهما؟ تحليل تحت الغطاء

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

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

ابدأ الدورة

مقدمة: لغز "mut" المزدوج

\n

إذا كنت قد كتبت بلغة Rust من قبل، فبالتأكيد رأيت الازدواجية الغريبة للكلمة المفتاحية mut. يمكن أن تأتي قبل اسم المتغير (let mut x = 5)، أو بعد النقطتين، بجانب النوع (&mut T). للوهلة الأولى - شيء بسيط، لكن هذا الموضوع حصد آلاف المشاهدات على Stack Overflow. لماذا؟ لأنه خلف هذا البساطة يكمن فرق جوهري بين الملكية والمرجع.

\n

في هذه المقالة، لن نجيب فقط على سؤال "أين نضع mut؟". سننظر تحت غطاء Rust، ونحلل كيف يعالج المترجم قابلية التغيير، ولماذا هذا الفرق هو مفتاح العمل الآمن مع الذاكرة. إذا كنت تتعلم Rust أو تريد فهمًا أعمق لنموذج الملكية الخاص بها - فأنت في المكان الصحيح.

\n\n

الجزء الرئيسي: عالمين - mut واحد

\n

1. mut الأول: "أنا المالك ويمكنني التغيير"

\n

عندما تكتب:

\n
let mut x = 42;\nx = 10; // صحيح\n
\n

فأنت تقول للمترجم: "أنا أنشئ متغيرًا x يملك القيمة 42، وأسمح بتغيير هذه القيمة لاحقًا". بدون mut، يكون المتغير غير قابل للتغيير (ثابت) افتراضيًا. هذا اختيار تصميمي واعٍ في Rust: الافتراضي هو الأمان، والتغيير هو إذن صريح.

\n

تحت الغطاء: يخصص المترجم مساحة على المكدس (أو الكومة إذا كان Box) ويضع علامة على خلية الذاكرة على أنها "قابلة للتغيير". ستراقب مدققات الاقتراض (borrow checker) لضمان عدم وجود مراجع قابلة للتغيير وغير قابلة للتغيير لهذه الذاكرة في نفس الوقت. إذا لم يكن mut موجودًا، فإن أي محاولة لتعيين قيمة جديدة ستؤدي إلى خطأ في الترجمة.

\n\n

2. mut الثاني: "أعطيك المفاتيح، لكنني لا أتخلى عن المنزل"

\n

الآن مثال مع مرجع:

\n
let mut x = 42;\nlet y = &mut x; // مرجع قابل للتغيير إلى x\n*y = 100;\n
\n

هنا &mut T لا يتعلق بالملكية، بل بـ الاقتراض. نقول: "أنا أعير x للمتغير y، مع إعطاء الحق في تغييره". يظل x نفسه هو المالك، لكنه ينقل السيطرة مؤقتًا.

\n

الفرق الرئيسي: mut بعد النقطتين يتعلق بـ نوع المرجع، وليس بالمتغير. لا يمكنك كتابة let &mut x = &y - فهذا غير صحيح نحويًا. يميز Rust بدقة بين:

\n
    \n
  • let mut x - متغير مالك قابل للتغيير
  • \n
  • &mut T - مرجع قابل للتغيير إلى قيمة من النوع T
  • \n
\n

هذا هو بالضبط ما يربك المبتدئين: بصريًا، "mut" في أماكن مختلفة، ودلاليًا - يحل مهامًا مختلفة.

\n\n

3. لماذا هذا مهم: مدقق الاقتراض (borrow checker)

\n

تخيل أنك تكتب دالة تأخذ مرجعًا:

\n
fn increment(n: &mut i32) {\n    *n += 1;\n}\n\nfn main() {\n    let mut a = 5;\n    increment(&mut a);\n    println!("{}", a); // 6\n}\n
\n

إذا كتبنا fn increment(n: &i32) (بدون mut)، فلن نتمكن من تغيير n. وإذا نسينا mut في a - فلن نتمكن من تمرير مرجع قابل للتغيير. يحميك مترجم Rust من التغيير العرضي للبيانات الذي قد يؤدي إلى سباق البيانات أو سلوك غير محدد.

\n

تحت الغطاء: &mut T هو مرجع فريد (حصري). في أي لحظة زمنية، يمكن أن يوجد مرجع واحد فقط قابل للتغيير على البيانات. تضمن هذه القاعدة عدم قراءة أي شخص آخر للبيانات أثناء تغييرها. يمكن أن يكون هناك أي عدد من المراجع غير القابلة للتغيير - فهي تقرأ فقط.

\n\n

4. مثال عملي حقيقي: الهياكل والطرق

\n

غالبًا ما يحدث الارتباك عند العمل مع الطرق:

\n
struct Point {\n    x: i32,\n    y: i32,\n}\n\nimpl Point {\n    fn shift(&mut self, dx: i32, dy: i32) {\n        self.x += dx;\n        self.y += dy;\n    }\n}\n\nfn main() {\n    let mu                

المدونات

توصيات الكتب