لماذا يشتكي Rust من الاقتراض وكيفية إصلاح ذلك؟ تحليل كامل

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

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

ابدأ الدورة

مقدمة: اللحظة التي لا يفهمك فيها Rust

\n

أنت تكتب بلغة Rust. كل شيء يسير على ما يرام. المترجم صامت. تشعر وكأنك سيد الذاكرة. ثم فجأة! يظهر هذا:

\n
error[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

أو قريبته:

\n
error[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
\n

يحدث خطأ cannot move out of borrowed content تحديدًا عندما تحاول نقل قيمة لديك عليها فقط مرجع (&T). يمنع Rust ذلك لأنه بعد النقل سيصبح المرجع "معلقًا" (dangling pointer). وكما تعلم، Rust لا يتسامح مع مثل هذه الأمور.

\n\n

السيناريو 1: محاولة نقل حقل هيكل عبر &self

\n

الحالة الأكثر شيوعًا. لديك هيكل، وتريد إرجاع أحد حقوله من دالة تأخذ &self.

\n\n

الكود مع الخطأ

\n
struct 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

سيقول المترجم:

\n
error[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

هناك ثلاث طرق رئيسية، حسب مهمتك.

\n\n

الطريقة 1: الاستنساخ (الأسهل، لكن ليس دائمًا الأكثر كفاءة)

\n

إذا كنت بحاجة إلى القيمة نفسها وليس مرجعًا، وكنت مستعدًا لدفع ثمن النسخ:

\n
fn give_name(&self) -> String {\n    self.name.clone() // يعمل! ننشئ نسخة.\n}\n
\n

العيب: clone() لـ String ينشئ نسخة كاملة من البيانات في الكومة. على السلاسل الطويلة، هذا مكلف.

\n\n

الطريقة 2: إرجاع مرجع (تغيير التوقيع)

\n

إذا كان الكود المستدعي يحتاج فقط إلى مرجع للحقل:

\n
fn give_name(&self) -> &String {\n    &self.name // نرجع مرجعًا، لا نقل\n}\n
\n

الآن نحن لا ننقل، بل نستعير. كل شيء عادل. سيحصل الكود المستدعي على

المدونات

توصيات الكتب