أهم أخطاء مطوري Rust المبتدئين: من الألف إلى الياء
\n\nRust هي لغة برمجة قوية وآمنة، لكن نظام الملكية والاقتراض الصارم فيها غالبًا ما يربك المبتدئين. في هذه المقالة، سنحلل الأخطاء الأكثر شيوعًا التي يواجهها المطورون المبتدئون في Rust. كل خطأ مصحوب بمثال كود، وشرح، وحل صحيح. هيا بنا!
\n\n1. تجاهل نظام الملكية (Ownership)
\n\nالخطأ الأكثر شيوعًا هو محاولة استخدام قيمة بعد نقلها. في Rust، يمكن أن يكون لكل قيمة مالك واحد فقط.
\n\n❌ كود خاطئ:
\nfn main() {\n let s1 = String::from("مرحباً");\n let s2 = s1;\n println!("{}", s1); // خطأ! s1 لم يعد يملك السلسلة النصية\n}\n\n\n✅ الحل الصحيح:
\nfn main() {\n let s1 = String::from("مرحباً");\n let s2 = s1.clone(); // نسخ صريح\n println!("{}", s1); // يعمل الآن\n println!("{}", s2);\n}\n\n\nنصيحة: استخدم .clone() فقط عندما يكون ذلك ضروريًا حقًا. بالنسبة للأنواع البدائية (i32, bool)، يتم النسخ تلقائيًا.
2. الخلط بين String و &str
\n\nغالبًا لا يفهم المبتدئون الفرق بين السلسلة المملوكة String والشريحة النصية &str. يؤدي هذا إلى أخطاء عند تمرير السلاسل النصية إلى الدوال.
❌ كود خاطئ:
\nfn print_message(msg: String) {\n println!("{}", msg);\n}\n\nfn main() {\n let greeting = "مرحباً بكم"; // &str\n print_message(greeting); // خطأ! يُتوقع String\n}\n\n\n✅ الحل الصحيح:
\nfn print_message(msg: &str) {\n println!("{}", msg);\n}\n\nfn main() {\n let greeting = "مرحباً بكم";\n print_message(greeting); // يعمل!\n \n let owned = String::from("العالم");\n print_message(&owned); // &String يتحول تلقائيًا إلى &str\n}\n\n\nالقاعدة الذهبية: في معاملات الدوال، استخدم &str إذا لم تكن بحاجة لنقل ملكية السلسلة النصية.
3. انتهاك قواعد الاقتراض (Borrowing)
\n\nلا يسمح Rust بوجود مراجع قابلة للتغيير وغير قابلة للتغيير في نفس الوقت لنفس الكائن. هذا يحمي من سباقات البيانات.
\n\n❌ كود خاطئ:
\nfn main() {\n let mut data = vec![1, 2, 3];\n let ref1 = &data;\n let ref2 = &mut data; // خطأ! يوجد بالفعل مرجع غير قابل للتغيير\n println!("{}", ref1);\n}\n\n\n✅ الحل الصحيح:
\nfn main() {\n let mut data = vec![1, 2, 3];\n {\n let ref1 = &data;\n println!("{}", ref1); // ref1 يعيش فقط في هذا الكتلة\n } // ref1 يخرج من النطاق\n let ref2 = &mut data; // الآن يمكن\n ref2.push(4);\n println!("{:?}", ref2);\n}\n\n\nهام: ينتهي نطاق المرجع عند آخر استخدام له. مترجم Rust ذكي بما يكفي لتتبع ذلك.
\n\n4. مشاكل مع زمن الحياة (Lifetimes)
\n\nالأخطاء المتعلقة بتعليقات زمن الحياة تخيف المبتدئين أكثر من غيرها. الأكثر شيوعًا هو إرجاع مرجع لمتغير محلي.
\n\n❌ كود خاطئ:
\nfn get_reference() -> &str {\n let local = String::from("اختبار");\n &local // خطأ! سيتم تدمير local بعد الخروج من الدالة\n}\n\n\n✅ الحل الصحيح (عبر الملكية):
\nfn get_reference() -> String {\n let local = String::from("اختبار");\n local // نعيد الملكية\n}\n\nfn main() {\n let s = get_reference();\n println!("{}", s);\n}\n\n\n✅ الحل الصحيح (عبر المعامل):
\nfn get_reference<'a>(input: &'a str) -> &'a str {\n input // نعيد المرجع بنفس زمن الحياة\n}\n\nfn main() {\n let data = String::from("اختبار");\n let result = get_reference(&data);\n println!("{}", re