لماذا الكود النظيف في Rust ليس رفاهية بل ضرورة
\n\nRust هي لغة تتطلب الانضباط من المطور. المترجم هنا صارم ولكنه عادل: لا يتسامح مع الإهمال في الذاكرة، لكنه يعطي في المقابل سرعة وأمانًا لا يصدقان. ومع ذلك، لتحقيق الإمكانات الحقيقية لـ Rust، لا يكفي مجرد جعل الكود يترجم. بل يجب كتابة كود نظيف — مفهوم، قابل للصيانة، وأنيق.
\n\nفي هذه المقالة، سنشرح كيفية كتابة كود نظيف في Rust بحيث لا يتحول مشروعك إلى متاهة من كتل unsafe والأنواع العامة المعقدة. ستتعرف على مبادئ التسمية، والتعابير الاصطلاحية للتعامل مع الملكية، ومعالجة الأخطاء، وهيكلة الكود. هيا بنا!
\n\n1. التسمية: تحدث بلغة Rust
\n\nالتسمية الصحيحة هي نصف النجاح. في Rust، هناك اتفاقيات صارمة، وانتهاكها يُعتبر بمثابة إشارة خطر (خاصة في مراجعة الكود).
\n\nالقواعد الأساسية
\n- \n
- snake_case للمتغيرات، الدوال، الدوال الأعضاء، والوحدات النمطية. \n
- PascalCase للأنواع (struct, enum, trait). \n
- SCREAMING_SNAKE_CASE للثوابت والمتغيرات الثابتة. \n
مثال:
\n\n// سيء\nlet UserName = "Alice";\nfn GetUserData() {}\nconst MAX_SIZE: u32 = 100;\n\n// جيد\nlet user_name = "Alice";\nfn get_user_data() {}\nconst MAX_SIZE: u32 = 100;\n\nتجنب الاختصارات (إذا لم تكن واضحة)
\nبدلاً من calc_avg()، من الأفضل كتابة calculate_average(). بدلاً من tmp، استخدم temp_buffer. Rust تشجع على الوضوح، وليس الإيجاز.
نصيحة: إذا كنت تكتب دالة تقبل إغلاقًا (closure)، استخدم الأسماء f أو g أو closure — فهذه ممارسة شائعة.
2. الملكية والاقتراض: كن صديقًا للمترجم
\n\nنظام الملكية هو قلب Rust. الكود النظيف يحترم قواعد ownership و borrowing، دون محاولة تجاوزها عبر clone() أو Rc بدون داعٍ.
القاعدة: فضّل الاقتراض على الملكية
\nإذا كانت الدالة لا تحتاج إلى أخذ الملكية، استقبل مرجعًا &T أو مرجعًا قابلًا للتغيير &mut T.
// سيء: clone غير ضروري\nfn process(data: Vec<i32>) {\n let mut sum = 0;\n for &val in &data {\n sum += val;\n }\n println!("المجموع: {}", sum);\n}\n\n// جيد: الاقتراض\nfn process(data: &[i32]) {\n let mut sum = 0;\n for &val in data {\n sum += val;\n }\n println!("المجموع: {}", sum);\n}\n\nتجنب الاستنساخ المفرط
\nإذا كنت بحاجة إلى تمرير البيانات إلى عدة أماكن، استخدم Rc (للسيناريوهات أحادية الخيط) أو Arc (للسيناريوهات متعددة الخيوط). لكن لا تفرط في استخدامها: في الحالات البسيطة، تكفي المراجع.
3. معالجة الأخطاء: تخلّ عن الذعر (panic)
\n\nفي Rust، هناك طريقتان رئيسيتان لمعالجة الأخطاء: panic! (خطأ غير قابل للاسترداد) و Result<T, E> (خطأ قابل للاسترداد). الكود النظيف يستخدم Result أينما أمكن. الذعر هو الملاذ الأخير للحالات الاستثنائية حقًا (مثل القسمة على صفر).
استخدم العامل ?
\nهذه طريقة موجزة لتمرير الخطأ إلى الأعلى. بدلاً من match الطويلة، اكتب:
use std::fs::File;\nuse std::io::Read;\n\nfn read_file(path: &str) -> Result<String, std::io::Error> {\n let mut file = File::open(path)?;\n let mut contents = String::new();\n file.read_to_string(&mut contents)?;\n Ok(contents)\n}\n\nأنشئ أنواع أخطاء خاصة بك
\nبدلاً من استخدام String كخطأ، عرّف تعدادًا (enum):
#[derive(Debug)]\nenum MyError {\n Io(std::io::Error),\n Parse(std::num::ParseIntError),\n}\n\nimpl From<std::io::Error> for My