الذاكرة وجامع القمامة في Rust: كيف تدير لغة بدون GC الموارد

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

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

ابدأ الدورة

لماذا لا تحتاج Rust إلى جامع قمامة ولكنها آمنة؟

\n\n

عندما يسمع المطورون المعتادون على Python أو Java أو C# عن Rust لأول مرة، غالبًا ما يخيفهم غياب جامع القمامة (GC) المعتاد. «كيف ذلك؟ — يفكرون. — بدون GC سنعود إلى عصر الإدارة اليدوية للذاكرة، والتسريبات، وأخطاء التجزئة؟»

\n\n

المفاجأة: لا، لن نعود. تقدم Rust طريقًا ثالثًا — نظام الملكية (ownership) الذي يضمن أمان الذاكرة في مرحلة التجميع. لا وقت تشغيل، ولا GC، ولا مؤشرات معلقة. فقط قواعد واضحة ومترجم لن يسمح لك بإطلاق النار على قدمك.

\n\n

في هذه المقالة، سنشرح كيف تعمل الذاكرة في Rust، وما هو RAII، والملكية، والاقتراض، ومدة الحياة، ولماذا كل هذا يجعل Rust واحدة من أكثر لغات البرمجة النظامية موثوقية.

\n\n

كيف تعمل الذاكرة في Rust: المكدس والكومة

\n\n

كما في أي لغة منخفضة المستوى، تنقسم الذاكرة في Rust إلى منطقتين رئيسيتين:

\n\n
    \n
  • المكدس (stack) — ذاكرة سريعة ومحدودة الحجم تعمل وفق مبدأ LIFO. هنا تُخزَّن المتغيرات المحلية ووسائط الدوال وعناوين العودة.
  • \n
  • الكومة (heap) — ذاكرة أبطأ ولكنها مرنة، تُستخدم للبيانات التي لا يُعرف حجمها في مرحلة التجميع (مثل String وVec وBox).
  • \n
\n\n

في C++، أنت من يقرر متى تخصص وتحرر الكومة (عبر new/delete). في Java أو Python، يقوم جامع القمامة بذلك نيابة عنك، ويوقف البرنامج بشكل دوري. أما Rust فتستخدم نظام الملكية الذي يحرر الذاكرة تلقائيًا بمجرد خروج المتغير من نطاق الرؤية.

\n\n

مثال: تخصيص الذاكرة الأساسي

\n\n
fn main() {\n    let s = String::from("مرحبًا بالعالم!"); // تخصيص ذاكرة في الكومة\n    println!("{}", s);\n} // هنا يخرج s من نطاق الرؤية، وتُحرر الذاكرة
\n\n

لاحظ: لا free() ولا delete. في نهاية الكتلة، يُدرج المترجم ضمنيًا استدعاءً للمدمر — drop(). يُسمى هذا RAII (اكتساب الموارد عند التهيئة) — يُلتقط المورد عند التهيئة ويُحرر عند تدمير الكائن.

\n\n

الملكية (Ownership): القاعدة الرئيسية في Rust

\n\n

نظام الملكية في Rust مبني على ثلاث قواعد بسيطة:

\n\n
    \n
  1. لكل قيمة في Rust مالك واحد (owner) بالضبط.
  2. \n
  3. عندما يخرج المالك من نطاق الرؤية، تُحذف القيمة.
  4. \n
  5. يمكن نقل المالك (move) أو إعارته مؤقتًا (borrow).
  6. \n
\n\n

يبدو مملًا؟ دعنا نرى كيف يعمل هذا عمليًا.

\n\n

النقل (Move)

\n\n
fn main() {\n    let s1 = String::from("hello");\n    let s2 = s1; // يُنقل s1 إلى s2\n    // println!("{}", s1); // خطأ! s1 لم يعد يملك البيانات\n    println!("{}", s2); // صحيح\n}
\n\n

في C++، كان هذا الإسناد سيؤدي إلى مؤشرين يشيران إلى نفس الذاكرة — وإلى تحرير مزدوج. في Rust، يمنعك المترجم ببساطة من استخدام s1 بعد النقل. لا تسريبات، ولا حذف مزدوج.

\n\n

النسخ (Copy)

\n\n

الأنواع البسيطة، مثل الأعداد الصحيحة والقيم المنطقية والأحرف، تطبق الخاصية Copy. بالنسبة لها، يُنشئ الإسناد نسخة كاملة:

\n\n
fn main() {\n    let x = 42;\n    let y = x;\n    println!("x = {}, y = {}", x, y); // كلاهما متاح!\n}
\n\n

لماذا؟ لأن i32 يُخزن على المكدس، ونسخه هو مجرد نسخ بضع بايتات. رخيص وآمن.

\n\n

الاقتراض (Borrowing) والمراجع

\n\n

ماذا تفعل إذا كنت بحاجة إلى إعطاء وصول مؤقت إلى البيانات دون نقل الملكية؟ استخدم المراجع. تتيح لك Rust اقتراض القيمة عبر مرجع.

\n\n
fn main() {\n    let s = String::from(                

المدونات

توصيات الكتب