مقدمة: اللحظة التي لا يفهمك فيها Rust
\nأنت تكتب بلغة Rust. كل شيء يسير على ما يرام. المترجم صامت. تشعر وكأنك سيد الذاكرة. ثم فجأة! يظهر هذا:
\nerror[E0507]: cannot move out of borrowed content\n --> src/main.rs:10:9\n |\n10 | let value = self.data;\n | ^^^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait\n\nأو قريبته:
\nerror[E0507]: cannot move out of `*self` which is behind a shared reference\n\nإذا كنت مبتدئًا، يبدو هذا وكأنه لعنة سحرية. إذا كنت خبيرًا، فأنت تعلم أنها مجرد درس آخر من Borrow Checker (مدقق الاقتراض).
\nفي هذه المقالة، سنشرح معنى هذه الأخطاء، ولماذا Rust صارم جدًا، والأهم من ذلك — كيفية إصلاحها بأمثلة كود حية. هذا ليس نظرية مملة، بل دليل عملي للبقاء في عالم الملكية والاقتراض.
\n\nباختصار عن الأساسيات: الملكية، الاقتراض و"النقل"
\nقبل الخوض في التفاصيل، دعنا نتذكر الركائز الثلاث لـ Rust:
\n- \n
- الملكية (Ownership): لكل قيمة في Rust مالك واحد فقط. \n
- الاقتراض (Borrowing): يمكنك "استعارة" قيمة عبر مرجع (&T — غير قابل للتغيير، &mut T — قابل للتغيير)، دون أخذ الملكية. \n
- النقل (Move): عندما تعين قيمة لمتغير جديد أو تمررها لدالة بدون &، تنتقل الملكية. يصبح المتغير الأصلي غير صالح. \n
يحدث خطأ cannot move out of borrowed content تحديدًا عندما تحاول نقل قيمة لديك عليها فقط مرجع (&T). يمنع Rust ذلك لأنه بعد النقل سيصبح المرجع "معلقًا" (dangling pointer). وكما تعلم، Rust لا يتسامح مع مثل هذه الأمور.
السيناريو 1: محاولة نقل حقل هيكل عبر &self
\nالحالة الأكثر شيوعًا. لديك هيكل، وتريد إرجاع أحد حقوله من دالة تأخذ &self.
الكود مع الخطأ
\nstruct User {\n name: String,\n age: u32,\n}\n\nimpl User {\n // نحاول إعطاء name "نهائيًا"\n fn give_name(&self) -> String {\n self.name // خطأ!\n }\n}\n\nfn main() {\n let user = User { name: "Alice".to_string(), age: 30 };\n let name = user.give_name();\n println!("{}", name);\n}\n\nسيقول المترجم:
\nerror[E0507]: cannot move out of `self.name` which is behind a shared reference\n\nلماذا؟ self هو &User. نحن لا نملك self، بل استعرناه فقط. محاولة نقل self.name (نوع String، الذي لا يطبق Copy) تعني أنه بعد استدعاء الدالة، سيفقد المالك (user) حقله. لكن المرجع &self لا يزال موجودًا! Rust يحمينا من الكارثة.
كيفية الإصلاح؟
\nهناك ثلاث طرق رئيسية، حسب مهمتك.
\n\nالطريقة 1: الاستنساخ (الأسهل، لكن ليس دائمًا الأكثر كفاءة)
\nإذا كنت بحاجة إلى القيمة نفسها وليس مرجعًا، وكنت مستعدًا لدفع ثمن النسخ:
\nfn give_name(&self) -> String {\n self.name.clone() // يعمل! ننشئ نسخة.\n}\n\nالعيب: clone() لـ String ينشئ نسخة كاملة من البيانات في الكومة. على السلاسل الطويلة، هذا مكلف.
الطريقة 2: إرجاع مرجع (تغيير التوقيع)
\nإذا كان الكود المستدعي يحتاج فقط إلى مرجع للحقل:
\nfn give_name(&self) -> &String {\n &self.name // نرجع مرجعًا، لا نقل\n}\n\nالآن نحن لا ننقل، بل نستعير. كل شيء عادل. سيحصل الكود المستدعي على