كيف تجتاز مقابلة Rust: أهم الأسئلة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا تعتبر لغة Rust صارمة جدًا في المقابلات؟

Rust هي لغة برمجة أنظمة تشتهر بأمان الذاكرة والأداء العالي. مُجمّع Rust صارم ولكنه عادل: فهو لن يسمح بخطأ في الملكية (ownership) أو مرجع معلق (dangling reference). لهذا السبب، نادرًا ما يُسأل في مقابلات Rust عن بناء الجملة "من الورق". بدلاً من ذلك، يختبر القائمون بالمقابلة الفهم العميق لنموذج الذاكرة، وإدارة الموارد، والكود الاصطلاحي (idiomatic code).

في هذه المقالة، سنستعرض أفضل 10 أسئلة يتم طرحها غالبًا في مقابلات مطوري Rust من المستوى المبتدئ والمتوسط. كل سؤال مصحوب بشرح ومثال كود. إذا كنت تستعد لمقابلة، فستكون هذه المقالة بمثابة قائمة التحقق الخاصة بك.



1. الملكية (Ownership) والاقتراض (Borrowing)

هذا هو أساس Rust. بدون فهم الملكية، لن تجتاز المقابلة. قد يطلب منك القائم بالمقابلة شرح الفرق بين String و &str أو يسأل لماذا لا يتم تجميع الكود.



سؤال: اشرح ما هي الملكية وكيف يعمل نقل البيانات إلى الدوال.

الإجابة: في Rust، كل قيمة لها مالك واحد فقط. عندما يخرج متغير عن النطاق، يتم حذف القيمة (drop). النقل إلى دالة يعني نقل (move) الملكية، إذا كان النوع لا يطبق الخاصية (trait) Copy.

fn main() {    let s1 = String::from("hello");    let s2 = s1; // تم نقل s1 إلى s2    // println!("{}", s1); // خطأ! s1 لم يعد يملك السلسلة    println!("{}", s2);}

إذا كنت تريد نقل البيانات دون فقدان الملكية، استخدم المراجع (&):

fn main() {    let s1 = String::from("hello");    let len = calculate_length(&s1); // اقتراض    println!("طول '{}' يساوي {}.", s1, len);}

fn calculate_length(s: &String) -> usize { s.len()}


سؤال: ما هو الاقتراض القابل للتغيير (mutable borrowing) ولماذا يمكن أن يكون هناك اقتراض واحد فقط؟

الإجابة: الاقتراض القابل للتغيير (&mut) يسمح بتغيير القيمة من خلال المرجع. يضمن المُجمّع وجود مرجع قابل للتغيير واحد فقط في النطاق، لمنع سباقات البيانات (data races).

fn main() {    let mut s = String::from("hello");    let r1 = &mut s;    // let r2 = &mut s; // خطأ! لا يمكن وجود مرجعين قابلين للتغيير    r1.push_str(", world");    println!("{}", r1);}


2. الأعمار (Lifetimes)

الأعمار هي ما يخيف المبتدئين. في المقابلة، سيُطلب منك شرح سبب الحاجة إلى تعليقات 'a وكيف يستنتج المُجمّع الأعمار.



سؤال: متى نحتاج إلى تحديد الأعمار بشكل صريح؟

الإجابة: عندما تُرجع دالة مرجعًا، ولا يستطيع المُجمّع تحديد المدة التي سيبقى فيها حيًا بشكل مستقل. المثال الكلاسيكي هو دالة تُرجع أطول سلسلة من اثنتين:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {    if x.len() > y.len() { x } else { y }}

fn main() { let string1 = String::from("سلسلة طويلة"); let result; { let string2 = String::from("قصيرة"); result = longest(&string1, &string2); } // println!("{}", result); // خطأ! تم حذف string2، المرجع أصبح معلقًا}

التعليق 'a يقول: "عمر المرجع المُعاد يساوي أصغر عمر من أعمار المراجع المُدخلة".



سؤال: ما هو elision (قواعد حذف الأعمار)؟

الإجابة: يقوم المُجمّع تلقائيًا بإدراج الأعمار في الحالات البسيطة. إذا كانت الدالة تأخذ مرجعًا واحدًا وتُعيد مرجعًا، يتم استنتاج العمر. إذا كان هناك عدة مراجع مُدخلة، فيجب تحديدها بشكل صريح.



3. Enum, Pattern Matching و Option/Result

تستخدم Rust بشكل نشط الأنواع الجبرية (algebraic types)

المدونات

توصيات الكتب