مقدمة: لماذا تبدو Rust صعبة؟
Rust هي لغة برمجة أنظمة حديثة تضمن سلامة الذاكرة بدون جامع القمامة (garbage collector). ومع ذلك، فإن نظام الأنواع الصارم ونموذج الملكية (ownership) الفريد غالبًا ما يربك المبتدئين. إذا كنت قد بدأت للتو في تعلم Rust وترى باستمرار رسائل حمراء من المترجم (compiler)، فلا تيأس — فهذا أمر طبيعي. في هذه المقالة، سنشرح أكثر 10 أخطاء شيوعًا لمطوري Rust المبتدئين ونوضح كيفية إصلاحها.
1. تجاهل borrow checker
الخطأ الأكثر شيوعًا هو محاولة استخدام قيمة بعد نقلها (move) أو بعد إنشاء مرجع قابل للتغيير. يتابع مترجم Rust (borrow checker) بدقة قواعد الملكية.
المشكلة
let s1 = String::from("مرحبا");let s2 = s1; // تم نقل s1 إلى s2println!("{}", s1); // خطأ! s1 لم يعد يملك البياناتالحل
استخدم clone() إذا كنت بحاجة إلى نسخ البيانات، أو قم بتمرير المراجع &:
let s1 = String::from("مرحبا");let s2 = &s1; // استعارةprintln!("{} و {}", s1, s2); // يعمل!نصيحة: إذا كان المترجم يعترض على الملكية — فغالبًا ما تحتاج إلى مرجع، وليس نقلًا كاملاً.
2. الخلط بين String و &str
غالبًا لا يفهم المبتدئون الفرق بين النوع المالك String والشريحة &str. يؤدي هذا إلى أخطاء في الأنواع وتخصيصات غير ضرورية للذاكرة.
المشكلة
fn greet(name: &str) { println!("مرحبًا، {}!", name);}
let my_name = String::from("آنا");greet(my_name); // خطأ: متوقع &str، وُجد Stringالحل
قم بتمرير مرجع إلى String — سيتحول تلقائيًا إلى &str:
greet(&my_name); // يعمل بفضل تحويل Derefاستخدم String للملكية وتعديل السلاسل النصية، و &str للقراءة والتمرير إلى الدوال.
3. الإفراط في استخدام unwrap() و expect()
الطريقتان unwrap() و expect() هما طريقة سريعة لمعالجة Option أو Result، لكنهما تسببان الذعر (panic) عند حدوث خطأ. في كود الإنتاج (production code) هذا غير مقبول.
المشكلة
let number: Option<i32> = None;let value = number.unwrap(); // ذعر!الحل
استخدم match أو الطريقتين unwrap_or، unwrap_or_else:
let value = number.unwrap_or(0); // سيعيد 0 إذا كان None// أو باستخدام matchmatch number { Some(v) => println!("{}", v), None => println!("القيمة غير موجودة"),}بالنسبة لـ Result استخدم العامل ? لتمرير الخطأ إلى الأعلى.
4. الاستخدام الخاطئ للمراجع القابلة للتغيير
قاعدة "مرجع قابل للتغيير واحد أو مراجع غير قابلة للتغيير متعددة" يتم انتهاكها باستمرار.
المشكلة
let mut data = vec![1, 2, 3];let ref1 = &mut data;let ref2 = &mut data; // خطأ!println!("{}", ref1);الحل
حدد نطاق رؤية المراجع القابلة للتغيير باستخدام الأقواس المتعرجة:
let mut data = vec![1, 2, 3];{ let ref1 = &mut data; ref1.push(4);} // ref1 يخرج من النطاقlet ref2 = &mut data; // الآن يمكنref2.push(5);5. نسيان زمن الحياة (lifetimes)
عندما يطلب المترجم تحديد زمن الحياة بشكل صريح، يصاب المبتدئون بالحيرة. الخطأ الأكثر شيوعًا هو إرجاع مرجع لمتغير محلي.
المشكلة
fn get_ref() -> &str { let s = String::from("اختبار"); &s // خطأ: نعيد مرجعًا لمتغير محلي}الحل
إما أن تعيد النوع المالك String، أو تستقبل مرجعًا كوسيط (argument):
المدونات
توصيات الكتب
اقرأ أيضًا