Rust على LeetCode: حل المشكلات وتطوير المهارات

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

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

ابدأ الدورة

Rust على LeetCode: الانتقال من Python إلى التفكير النظامي

\n\n

إذا كنت قد دخلت إلى PythonLib، فمن المرجح أنك معتاد على Python. أنت تعرف كيفية كتابة حلول موجزة في سطر واحد وتستخدم الكتابة الديناميكية كأمر مسلم به. لكن العالم لا يقف ساكنًا. في كثير من الأحيان في المقابلات في الشركات الكبرى (وليس فقط) يُطلب منك كتابة كود بلغة Rust. لماذا؟ لأن Rust هي لغة تجمع بين سرعة C وأمان الذاكرة، وLeetCode هي ساحة مثالية لصقل هذه المهارة.

\n\n

في هذه المقالة، لن نمر فقط على بناء الجملة. سنقوم بتحليل كيفية التفكير في نموذج Rust عند حل المشكلات الخوارزمية، وما هي هياكل البيانات التي يجب استخدامها، وكيف لا تغرق في borrow checker. هيا بنا!

\n\n

لماذا LeetCode بلغة Rust هو تحدٍ وميزة؟

\n\n

معظم الحلول على LeetCode مكتوبة بلغة Python أو Java. أما Rust فتجبرك على التفكير في الذاكرة، والملكية، وعمر المتغيرات. يبدو هذا صعبًا، لكن هذا هو بالضبط ما يجعلك أقوى كمهندس. من خلال حل المشكلات بلغة Rust، تتعلم تلقائيًا:

\n\n
    \n
  • نظام الملكية (Ownership) — الميزة الرئيسية للغة.
  • \n
  • الاقتراض (Borrowing) والمراجع القابلة للتغيير/غير القابلة للتغيير.
  • \n
  • المكررات الفعالة دون تخصيصات زائدة.
  • \n
  • العمل مع Option و Result بدلاً من الاستثناءات.
  • \n
\n\n

إذا كنت قادمًا من Python، فستبدو لك المشكلات العشر الأولى بمثابة عذاب. ولكن عندما تفهم كيف يحميك المترجم من null pointer وسباقات البيانات، فستقع في حب هذه اللغة.

\n\n

ما الذي يجب تعلمه قبل البدء؟

\n\n

قبل أن تندفع في مجموعة المشكلات، دعنا نستعرض الأدوات. بدونها، ستكتب كودًا كما لو كنت تكتب بلغة C++ مع وحدات ماكرو — غير فعال ومؤلم.

\n\n

1. المكررات والإغلاقات (Closures)

\n

في Rust، المكررات ليست مجرد حلقة for. إنها أداة وظيفية قوية. بدلاً من كتابة for i in 0..vec.len()، تعلم استخدام .iter()، .map()، .filter()، .fold().

\n\n
// Python: [x*2 for x in list if x > 0]\n// Rust:\nlet result: Vec<i32> = list.iter()\n    .filter(|&x| *x > 0)\n    .map(|x| x * 2)\n    .collect();
\n\n

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

\n\n

2. HashMap و BTreeMap

\n

للمشكلات التي تتطلب حساب التكرارات (مثل "Two Sum" أو "Group Anagrams")، HashMap هو أفضل صديق لك. لكن تذكر: في Rust، يتطلب الإدراج في HashMap ملكية المفتاح. إذا كنت تستخدم &str، فسيتعين عليك استنساخ السلسلة أو استخدام String.

\n\n
use std::collections::HashMap;\n\nfn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {\n    let mut map = HashMap::new();\n    for (i, &num) in nums.iter().enumerate() {\n        let complement = target - num;\n        if let Some(&j) = map.get(&complement) {\n            return vec![j as i32, i as i32];\n        }\n        map.insert(num, i);\n    }\n    vec![]\n}
\n\n

لاحظ: enumerate() تُرجع زوجًا (usize, &i32). نستخدم &num لإلغاء مرجعية المؤشر.

\n\n

3. Vec والشرائح

\n

Vec هي مصفوفة ديناميكية. لكن في مشكلات LeetCode، غالبًا ما تحتاج إلى العمل مع المصفوفات الفرعية. لهذا، استخدم الشرائح &[T]. لا تتطلب نسخًا وتسمح بتمرير جزء من المصفوفة إلى دالة.

\n\n
fn max_subarray_sum(arr: &[i32]) -> i32 {\n    let mut max_ending = arr[0];\n    let mut max_so_far = arr[0];\n    for &x in arr[1..].iter() {\n        max_ending = std::cmp::max(x, max_ending + x);\n        max_so_far = std::cmp::max(max_so_far, max_ending);\n    }\n    max_so_far\n}
\n\n

أفضل 5 أخطاء شائعة

المدونات

توصيات الكتب