كيف تفرق بين التصحيح والإصدار في Rust؟ نشرح cfg بأمثلة واقعية
\n\nهل سبق لك أن كتبت كودًا في Rust وفكرت: "ليتني أضيف تسجيلاً مفصلاً في وضع التصحيح، وأزيل كل الزوائد في الإصدار"؟ أو ربما أردت تضمين التأكيدات فقط أثناء التطوير، ولكن إيقافها في البنية النهائية؟ إذا كان الأمر كذلك، فأنت في المكان الصحيح. اليوم سنشرح كيف يمكنك باستخدام سحر cfg التحكم في سلوك الكود بناءً على نوع البنية. هذه ليست مجرد نظرية جافة - سننتقل من علمة بسيطة إلى حل جاهز للإنتاج.
الموضوع ليس من فراغ: على Stack Overflow، سؤال "How to check release / debug builds using cfg in Rust?" حصد آلاف المشاهدات. هذا يعني أن الكثيرين يحتاجون إليه. وسنقوم بشرحه بالتفصيل.
\n\nلماذا هذا مهم؟ عالمين - نهجان مختلفان
\n\nفي Rust، هناك ملفان رئيسيان للبناء: التصحيح debug (مع العلمة --debug أو ببساطة cargo build) و الإصدار release (cargo build --release). لا يختلفان فقط في التحسينات، بل أيضًا في سلوك بعض الفحوصات المضمنة.
على سبيل المثال:
\n- \n
- التصحيح: بطيء، ولكن مع فحوصات حدود المصفوفات، ورموز التصحيح، والتأكيدات. \n
- الإصدار: سريع، ولكن بدون فحوصات إضافية - كل شيء من أجل الأداء. \n
ماذا لو كنت بحاجة إلى أن يكتب البرنامج في نسخة التصحيح كل إجراء في السجل، وفي الإصدار فقط الأخطاء الحرجة؟ أو أن يكون نقطة النهاية الاختبارية متاحة محليًا فقط؟ لهذا يوجد cfg.
الأدوات الأساسية: cfg!() و #[cfg()]
\n\nفي Rust، هناك طريقتان للتحقق من نوع البنية:
\n- \n
- الماكرو
cfg!()- يتم تنفيذه أثناء التجميع، ويعيد قيمةbool. يُستخدم داخل الدوال. \n - السمة
#[cfg()]- تقوم بتضمين أو استبعاد كتلة كاملة من الكود من التجميع. \n
كلاهما يستخدم نفس المفاتيح. الأكثر شيوعًا لمهمتنا هو debug_assertions. يكون نشطًا فقط في بنية التصحيح. وهناك أيضًا العكس - not(debug_assertions) للإصدار.
مثال 1: أبسط فحص داخل دالة
\n\nلنبدأ بماكرو cfg!(). لنفترض أن لدينا دالة لحساب شيء معقد. في التصحيح، نريد التحقق من بيانات الإدخال:
fn calculate_something(x: i32) -> i32 {\n // التحقق فقط في بنية التصحيح\n if cfg!(debug_assertions) {\n println!("[DEBUG] تم استدعاء calculate_something بـ x = {}", x);\n assert!(x > 0, "يجب أن يكون x موجبًا!");\n }\n\n // المنطق الأساسي\n x * 2\n}\n\nfn main() {\n let result = calculate_something(10);\n println!("النتيجة: {}", result);\n}\n\n\nماذا سيحدث؟
\n- \n
- عند
cargo build(تصحيح) - سترى السجل، وإذا كان x ≤ 0، سيتعطل البرنامج بتأكيد. \n - عند
cargo build --release- السطر الذي يحتوي علىcfg!(debug_assertions)سيتحول إلىfalse، وسيقوم المترجم بإزالة هذه الكتلة ككود ميت. لا يوجد حمل زائد! \n
مثال 2: الاستبعاد الكامل للكود باستخدام #[cfg()]
\n\nأحيانًا تحتاج ليس فقط إلى تخطي التنفيذ، بل إلى إزالة دالة أو وحدة نمطية بالكامل من الملف الثنائي. لهذا نستخدم السمة:
\n\n// سيتم تجميع هذه الوحدة فقط في بنية التصحيح\n#[cfg(debug_assertions)]\nmod debug_helpers {\n pub fn print_state(state: &str) {\n println!("[DEBUG] الحالة الحالية: {}", state);\n }\n\n pub fn validate_data(data: &[u8]) -> bool {\n // فحص مكلف، غير مطلوب في الإصدار\n data.len() < 1000\n }\n}\n\n// هذه الوحدة - فقط للإصدار\n#[cfg(not(debug_assertions))]\nmod release_helpers {\n pub fn print_state(_state: &str) {\n // في الإصدار لا نفعل شيئًا - نوفر وحدة المعالجة المركزية\n }\n\n pub fn validate_data(_data: &[u8]) ->