مقدمة: لماذا تعتبر Rust تحدياً للمقابل
\nRust هي لغة تتطلب من المطور فهماً عميقاً لإدارة الذاكرة ونظام الأنواع ونموذج التوازي. مقابلة Rust ليست اختباراً للصياغة، بل اختباراً لفهم فلسفة اللغة. في هذه المقالة، سنستعرض الأسئلة الرئيسية التي قد تُطرح عليك في المقابلة - من أساسيات الملكية إلى مواضيع متقدمة مثل البرمجة غير المتزامنة. ستتعرف على الإجابات التي يتوقعها المقابل وكيفية تجنب الأخطاء الشائعة.
\n\n1. الملكية والاقتراض وزمن الحياة
\n1.1. ما هي الملكية (ownership) وكيف تعمل؟
\nهذا هو أساس Rust. كل قيمة لها مالك واحد فقط. عندما يخرج المالك من نطاق الرؤية، يتم حذف القيمة. هذا يتجنب الحاجة إلى جامع القمامة ويضمن سلامة الذاكرة في مرحلة الترجمة.
\nfn main() {\n let s1 = String::from("مرحباً");\n let s2 = s1; // s1 تنتقل إلى s2\n // println!("{}", s1); // خطأ! s1 لم تعد صالحة\n println!("{}", s2); // يعمل\n}\nنصيحة: اشرح الفرق بين النقل (move) والنسخ (copy). بالنسبة للأنواع التي تطبق trait Copy (مثل الأعداد الصحيحة)، لا يحدث نقل - بل يتم نسخ البيانات.
1.2. قواعد الاقتراض (borrowing)
\nالاقتراض يسمح باستخدام مرجع للبيانات دون نقل ملكيتها. هناك نوعان من المراجع:
\n- \n
- غير قابلة للتغيير (&T): يمكن أن يكون أي عدد منها، لا يمكن تغيير البيانات. \n
- قابلة للتغيير (&mut T): مرجع واحد فقط للبيانات في النطاق الحالي. \n
fn main() {\n let mut s = String::from("hello");\n let r1 = &s; // مرجع غير قابل للتغيير\n let r2 = &s; // مرجع آخر غير قابل للتغيير - مسموح\n // let r3 = &mut s; // خطأ! لا يمكن وجود مراجع غير قابلة للتغيير وقابلة للتغيير معاً\n println!("{} {}", r1, r2);\n}\nسؤال خادع: "لماذا تمنع Rust وجود مراجع قابلة للتغيير وغير قابلة للتغيير معاً؟" الإجابة: لمنع سباقات البيانات (data races) في مرحلة الترجمة.
\n\n1.3. زمن الحياة (lifetimes) - كيف تشرحه بكلمات بسيطة
\nزمن الحياة هو تعليقات توضيحية تخبر المترجم بمدة صلاحية المراجع. غالباً ما يستنتجها المترجم بنفسه، لكن في الدوال المعقدة يجب تحديدها صراحة.
\nfn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n if x.len() > y.len() { x } else { y }\n}\nالفكرة الأساسية: 'a تعني أن المرجع المُعاد سيعيش لمدة لا تتجاوز كلا المرجعين المُدخلين. هذا يضمن أن المرجع لن يصبح معلقاً (dangling).
2. التعدادات، مطابقة الأنماط و traits
\n2.1. Enum في Rust - لماذا هو أقوى من اللغات الأخرى
\nفي Rust، يمكن للتعدادات تخزين بيانات من أي نوع - نصوص، أرقام، هياكل، وحتى تعدادات أخرى. هذا يسمح بإنشاء أنواع بيانات جبرية (ADT).
\nenum Message {\n Quit,\n Move { x: i32, y: i32 },\n Write(String),\n ChangeColor(i32, i32, i32),\n}\nسؤال نموذجي: "كيف يعمل Option<T> ولماذا لا يوجد null في Rust؟" الإجابة: Option هو enum يمكن أن يكون Some(T) أو None. المترجم يجبرك على معالجة كلا الخيارين، مما يزيل أخطاء المؤشرات الفارغة.
2.2. مطابقة الأنماط (pattern matching)
\nبناء match هو قلب Rust. يجب أن يكون شاملاً (exhaustive).
fn describe_point(point: (i32, i32)) -> &'static str {\n match point {\n (0, 0) => "نقطة الأصل",\n (0, _) => "على المحور X",\n (_, 0) => "على المحور Y",\n _ => "في مكان ما بالمستوى",\n }\n}\nحيلة للمقابلة: استخدم if let لـ