تخطَّ إلى المحتوى

٢١ يوليو ٢٠٢٦

حل أخطاء Rust الشائعة للمبتدئين بالعربي

شارك المقال:

فاحص الاستعارة (borrow checker) هو سرّ أمان Rust، لكنه أيضًا أكثر ما يحبط المبتدئين في أوّل أسبوعين. إليك أشهر أربعة أخطاء ترجمة وحلّها.

كيف تقرأ خطأ Rust؟

رسالة الخطأ في Rust غنية جدًا: تعطيك رمز الخطأ (مثل E0382)، السطر بالضبط، وغالبًا اقتراح حلّ جاهز. اقرأ السطر الأول (نوع الخطأ)، ثم الشرح تحته الذي يشير للسطر المسبّب والسطر الذي "استهلك" القيمة.

1) E0382 — use of moved value

let s1 = String::from("مرحبًا");
let s2 = s1;              // الملكية انتقلت إلى s2
println!("{}", s1);       // ❌ error[E0382]: use of moved value

السبب: String ليست من الأنواع القابلة للنسخ التلقائي (Copy)، فـ let s2 = s1 ينقل الملكية بدل نسخها. بعدها s1 لم يعد صالحًا.

الحلّ: استعِر بدل النقل، أو استنسخ صراحةً إن احتجت نسختين مستقلّتين:

let s1 = String::from("مرحبًا");
let s2 = &s1;              // ✅ استعارة، لا نقل
println!("{} {}", s1, s2);

// أو
let s3 = s1.clone();       // ✅ نسخة مستقلّة صريحة

2) E0499 — cannot borrow as mutable more than once

let mut x = 5;
let r1 = &mut x;
let r2 = &mut x;          // ❌ error[E0499]
println!("{r1} {r2}");

السبب: Rust تسمح إمّا بمرجع قابل للتعديل واحد، أو عدّة مراجع للقراءة فقط — وليس الاثنين معًا، لمنع تسابق البيانات وقت الترجمة.

الحلّ: أنهِ استخدام المرجع الأول قبل إنشاء الثاني (انتهاء نطاقه):

let mut x = 5;
{
    let r1 = &mut x;
    *r1 += 1;
}                          // r1 انتهى نطاقه هنا
let r2 = &mut x;           // ✅ الآن مسموح
println!("{r2}");

3) E0502 — cannot borrow as mutable because also borrowed as immutable

fn add_one(v: &mut Vec<i32>) {
    let first = &v[0];        // استعارة للقراءة
    v.push(4);                 // ❌ error[E0502]: استعارة تعديل أثناء وجود قراءة نشطة
    println!("{first}");
}

السبب: نفس المبدأ السابق: first استعارة قراءة نشطة، وv.push(4) يحتاج استعارة تعديل — لا يجتمعان.

الحلّ: أنهِ استخدام المرجع للقراءة قبل التعديل:

fn add_one(v: &mut Vec<i32>) {
    let first = v[0];    // انسخ القيمة (i32 قابل للنسخ) بدل استعارتها
    v.push(4);            // ✅ لا تعارض الآن
    println!("{first}");
}

4) E0308 — mismatched types

fn plus_one(x: i32) -> i32 {
    x + 1
}

plus_one("5");    // ❌ error[E0308]: expected `i32`, found `&str`

السبب: Rust لا تحوّل الأنواع تلقائيًا بين النصوص والأعداد كما تفعل بعض اللغات المفسَّرة — كل تحويل يجب أن يكون صريحًا.

الحلّ: حوّل النوع صراحة قبل الاستدعاء:

let n: i32 = "5".parse().unwrap();
plus_one(n);       // ✅

جدول الأخطاء السريع

الرمزالمعنىالحلّ السريع
E0382استخدام قيمة بعد نقل ملكيتهااستعِر بـ & أو استخدم .clone()
E0499مرجعا تعديل نشطان بنفس الوقتأنهِ نطاق الأول قبل إنشاء الثاني
E0502تعارض بين استعارة قراءة وتعديلافصل بين نطاقي القراءة والتعديل
E0308عدم تطابق الأنواعحوّل النوع صراحةً (مثل .parse())

الخلاصة

كل هذه الأخطاء تعبير عن نفس الفكرة: مالك واحد، أو عدّة قرّاء، أو معدِّل واحد — لا أكثر في نفس اللحظة. بمجرّد استيعاب هذه القاعدة تتحوّل رسائل الخطأ من إحباط إلى دليل شبه مباشر للحلّ.

ابدأ مسار Rust من الصفر.

📚 مصادر رسمية للتعمّق: كتاب Rust الرسمي

الأسئلة الشائعة

لماذا يرفض مترجم Rust كودًا يعمل في لغات أخرى؟

لأن Rust تتحقّق من صحّة استخدام الذاكرة وقت الترجمة عبر نظام الملكية والاستعارة، فترفض أنماطًا قد تسبّب أخطاء ذاكرة أو تسابق بيانات في لغات أخرى تسمح بها ثم تكتشف المشكلة وقت التشغيل فقط (أو لا تكتشفها إطلاقًا).

ما الفرق بين use of moved value وcannot borrow as mutable؟

use of moved value (E0382) يعني أنك استخدمت متغيّرًا بعد أن انتقلت ملكيته لمتغيّر آخر. أما cannot borrow as mutable (E0499/E0502) فيعني أنك حاولت إنشاء استعارة متعارضة مع استعارة أخرى ما زالت نشطة لنفس القيمة.

هل clone() هو الحلّ الدائم لأخطاء الملكية؟

clone() يحلّ المشكلة سريعًا لأنه ينشئ نسخة مستقلة، لكنه يكلّف نسخًا فعليًا للبيانات. الأفضل غالبًا هو إعادة تنظيم الكود ليستخدم مراجع (&) بدل النقل، والاحتفاظ بـ clone() لحالات فعلًا تحتاج نسخًا مستقلًا.

اقرأ أيضًا

تصفّح كل المقالات في المدوّنة، أو ابدأ التعلّم من المسارات و خرائط الطريق.