Rust: لغة تجعلك تفكر بطريقة مختلفة
\n\nلقد قررت إتقان Rust. ممتاز. هذه ليست مجرد لغة أخرى - إنها تحدٍ. Rust يكسر الأنماط التي اعتدنا رؤيتها في Python أو JavaScript أو حتى C++. إنها تجعلك تفكر في الذاكرة والأمان والتزامن على مستوى جديد.
\n\nمقابلة Rust ليست امتحانًا في بناء الجملة. إنها اختبار لكيفية تفكيرك. يريد المحاورون رؤية أنك تفهم فلسفة اللغة: الملكية، الاقتراض، أوقات الحياة. في هذه المقالة، سنناقش الموضوعات والأسئلة الحقيقية التي سيُطرح عليك، وسنعرض أمثلة كود ستساعدك على اجتياز المقابلة.
\n\nهل أنت مستعد؟ لنبدأ.
\n\n1. الملكية (Ownership) - الأساس الذي لا يمكن الاستغناء عنه
\n\nالجزء الأول والأهم. إذا كنت لا تفهم الملكية - فأنت لا تفهم Rust. في المقابلة، سيُطلب منك بالتأكيد شرح كيفية عمل نقل الملكية، وكتابة كود يوضح ذلك.
\n\nالقواعد الأساسية للملكية:
\n- \n
- كل قيمة في Rust لها مالك واحد بالضبط. \n
- عندما يخرج المالك من النطاق، يتم حذف القيمة. \n
- يمكن نقل الملكية (move) أو إعارتها مؤقتًا (borrow). \n
مثال: ماذا سيطبع هذا الكود؟
\nfn main() {\n let s1 = String::from("hello");\n let s2 = s1;\n println!("{}", s1); // خطأ!\n}\n\nالإجابة الصحيحة: خطأ في التجميع. تم نقل s1 إلى s2، وأصبح s1 غير صالح. يريد المحاور سماع أنك تفهم دلالات النقل (move semantics) ولماذا Rust صارم جدًا في هذا الشأن.
2. الاقتراض والمراجع (Borrowing & References)
\n\nبعد الملكية - الاقتراض. كيف تأخذ مرجعًا دون سلب الملكية؟ والأهم - كيف لا تخالف قواعد المترجم.
\n\nقواعد الاقتراض:
\n- \n
- يمكن أن يكون هناك أي عدد من المراجع غير القابلة للتغيير (&T). \n
- يمكن أن يكون هناك مرجع واحد فقط قابل للتغيير (&mut T). \n
- لا يمكن أن يكون لديك &T و &mut T على نفس القيمة في نفس الوقت. \n
مثال: ابحث عن الخطأ
\nfn main() {\n let mut s = String::from("hello");\n let r1 = &s;\n let r2 = &mut s;\n println!("{} {}", r1, r2);\n}\n\nالخطأ: أنشأنا مرجعًا غير قابل للتغيير r1، ثم مرجعًا قابلًا للتغيير r2. لن يسمح المترجم بذلك. الشرح: هذه حماية من سباقات البيانات (data races) في مرحلة التجميع.
3. أوقات الحياة (Lifetimes) - الموضوع الذي يخيف الجميع
\n\nLifetimes ليست سحرًا. إنها تعليقات توضيحية تساعد المترجم على فهم المدة التي تظل فيها المراجع صالحة. في المقابلة، سيُطلب منك شرح سبب الحاجة إليها، وكتابة دالة مع معاملات lifetime صريحة.
\n\nسؤال نموذجي:
\nاكتب دالة تُرجع أطول سلسلة نصية من اثنتين. كيف ستحدد lifetimes؟
\n\nfn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n if x.len() > y.len() { x } else { y }\n}\n\nهنا 'a هو وقت الحياة المشترك الذي يضمن أن المرجع المُعاد سيكون صالحًا طالما أن كلا المرجعين المُدخلين صالحان. سيقدر المحاور إذا ذكرت إلغاء وقت الحياة (lifetime elision) - عندما يستنتج Rust lifetimes للحالات البسيطة بنفسه.
4. التعدادات (Enums) ومطابقة الأنماط (Pattern Matching) - قوة Rust
\n\nRust لا يحتوي على null. بدلاً من ذلك - Option<T> و Result<T, E>. في المقابلة، سيُطلب منك كتابة معالجة الأخطاء باستخدام match.
مثال: القسمة الآمنة
\nfn safe_divide(a: f64, b: f64) -> Option<f64> {\n if b == 0.0 { None } else { Some(a / b) }\n}\n\nfn main() {\n match safe_divide(10.0, 0.0) {\n Some(result) => println!("Result: {}", result),\n None => println!("Cannot divide by zero"),\n }\n}\n\nأظهر