مراجعة الكود في Rust: ما يجب التحقق منه حتى لا ينهار الكود

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

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

ابدأ الدورة

لماذا مراجعة الكود في Rust ليست مجرد فحص للأقواس

\n\n

Rust هي لغة تعد بالعمل الآمن مع الذاكرة والأداء العالي. ولكن حتى أكثر المترجمات صرامة لا تحمي من الأخطاء المنطقية، أو الكود غير الاصطلاحي، أو الاستخدام غير المبرر لـ unsafe. مراجعة الكود في Rust هي فن. أنت لا تنظر فقط إلى ما إذا كانت الأقواس المعقوفة مغلقة. أنت تتحقق من كيفية تجاوز المطور (أو عدم تجاوزه) لقيود borrow checker، ومدى كفاءة استخدامه للمكررات، وما إذا كان يحاول "خداع" النظام باستخدام المؤشرات الخام.

\n\n

في هذه المقالة، سأشرح الجوانب التي يجب الانتباه إليها عند مراجعة الكود في Rust. بدون نظرية زائدة - فقط نصائح عملية وأمثلة وقائمة مراجعة يمكنك تعليقها على الحائط (أو حفظها في الإشارات المرجعية). هيا بنا!

\n\n

1. الملكية، الاقتراض وأوقات الحياة: المصدر الرئيسي للألم

\n\n

أول ما يجب التحقق منه هو ما إذا كان نظام الملكية يعمل بشكل صحيح. حتى إذا تم تجميع الكود، فهذا لا يعني أنه صحيح. المشاكل النموذجية:

\n\n
    \n
  • الاستنساخ المفرط. عندما كان من الممكن استخدام مرجع، يقوم المطور بنسخ البيانات. هذا يقتل الأداء.
  • \n
  • الاقتراض القابل للتغيير وغير القابل للتغيير. إذا كان هناك &mut T و &T في نفس النطاق في نفس الوقت - سيكون هناك خطأ. لكن في بعض الأحيان يمكن إعادة ترتيب الكود لتجنب ذلك.
  • \n
  • أوقات الحياة غير الصحيحة (lifetimes). خاصة في الهياكل المعقدة التي تحتوي على مراجع متعددة.
  • \n
\n\n

مثال: كود سيء

\n
fn process(data: &Vec<i32>) -> i32 {\n    let mut sum = 0;\n    for &x in data.iter() {\n        sum += x;\n    }\n    // كل شيء على ما يرام، لكن لماذا تمرر &Vec إذا كان بإمكانك &[i32]؟\n    sum\n}
\n\n

كود جيد

\n
fn process(data: &[i32]) -> i32 {\n    data.iter().sum()\n}
\n\n

ما يجب التحقق منه في المراجعة: هل يتم تمرير &Vec أو &String أو &HashMap حيث يكفي slice؟ هل يتم استخدام clone() بدون ضرورة؟ هل تم وضع تعليقات أوقات الحياة بشكل صحيح؟

\n\n

2. Unsafe: المنطقة الحمراء

\n\n

كتل unsafe هي مثل فتحة مفتوحة في Rust الآمن. يجب أن تكون مبررة، وضئيلة، وموثقة بعناية. في مراجعة الكود، انتبه إلى:

\n\n
    \n
  • لماذا يتم استخدام unsafe؟ غالبًا ما يمكن الاستغناء عن التجريدات الآمنة.
  • \n
  • صحة المؤشرات الخام. هل يتم إلغاء مرجعية مؤشر فارغ؟ هل هناك مرجع معلق بعد تحرير الذاكرة؟
  • \n
  • الثوابت (invariants). يجب أن يصف الكود الذي يحتوي على unsafe بوضوح الثوابت التي يحافظ عليها.
  • \n
\n\n

مثال: unsafe خطير

\n
unsafe fn dangerous() {\n    let ptr = 0x12345 as *mut i32;\n    *ptr = 42; // ماذا لو لم يكن هناك شيء في هذا العنوان؟\n}
\n\n

هذا الكود يجب ألا يجتاز المراجعة. حتى لو "كان يعمل" - فهذه ليست حجة.

\n\n

Unsafe جيد

\n
unsafe fn init_buffer(buf: &mut [u8]) {\n    // نحن متأكدون من أن buf يشير إلى ذاكرة صالحة،\n    // لأن الطرف المستدعي يضمن ذلك.\n    std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len());\n}
\n\n

ما يجب التحقق منه: هل يوجد تعليق يشرح سبب كل unsafe؟ هل هناك اختبارات تتحقق من الحالات الحدودية؟ هل يتم انتهاك ضمانات الأمان؟

\n\n

3. الإغلاقات والأسلوب الوظيفي

\n\n

تشجع Rust الأسلوب الوظيفي: المكررات، الإغلاقات، map، filter، fold. لكن هذا قد يؤدي إلى التعقيد المفرط. في المراجعة، انظر إلى:

\n\n
    \n
  • هل يتم الإفراط في استخدام الإغلاقات؟ إذا كان الإغلاق يلتقط العديد من المتغيرات، فربما يجب استخراج المنطق في دالة منفصلة.

المدونات

توصيات الكتب