فاحص الاستعارة (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()) |
الخلاصة
كل هذه الأخطاء تعبير عن نفس الفكرة: مالك واحد، أو عدّة قرّاء، أو معدِّل واحد — لا أكثر في نفس اللحظة. بمجرّد استيعاب هذه القاعدة تتحوّل رسائل الخطأ من إحباط إلى دليل شبه مباشر للحلّ.