مقدمة: لماذا يستطيع Rust السحر بالكود؟
\n\nهل سبق لك أن كتبت كودًا بلغة Rust وشعرت أنك تكرر نفس الإنشاءات لعشرات الهياكل؟ أو هل أردت توليد تنفيذ trait تلقائيًا دون نسخ الكود يدويًا؟ مرحبًا بك في عالم البرمجة الوصفية في Rust — التقنيات التي تسمح لك بكتابة كود يكتب كودًا.
\n\nلا تمتلك Rust انعكاسًا كاملاً أثناء التنفيذ (مثل Java أو Python)، لكنها تقدم شيئًا أكثر قوة وأمانًا — الماكروسات. تعمل هذه في مرحلة التجميع، حيث تحلل وتحول شجرة التركيب المجردة (AST). وهذا يعطي إمكانيات لا تصدق للتحسين وتقليل الكود المتكرر دون فقدان الأداء.
\n\nفي هذه المقالة، سنشرح التقنيات المتقدمة للبرمجة الوصفية: من macro_rules! الكلاسيكية إلى الماكروسات الإجرائية الكاملة. ستتعلم كيفية إنشاء سمات الاشتقاق الخاصة بك وكتابة ماكروسات سمة تغير دلالات الكود. هيا بنا!
الماكروسات التصريحية: macro_rules! ليست مجرد "استبدال نص"
\n\nيعتقد الكثيرون أن macro_rules! هي معالج نص بدائي. في الواقع، إنها نظام مطابقة أنماط قوي يعمل على مستوى الرموز. تسمح بإنشاء لغات خاصة بالمجال (DSL) مباشرة داخل Rust.
مثال متقدم: إنشاء باني باستخدام ماكرو
\n\nتخيل أنك بحاجة إلى إنشاء "باني" (builder) لهيكل. بدلاً من كتابة عشرات الدوال، يمكنك كتابة ماكرو واحد:
\n\nmacro_rules! builder {\n ($struct_name:ident, { $($field:ident: $field_type:ty),* $(,)? }) => {\n struct $struct_name {\n $($field: Option<$field_type>),*\n }\n\n impl $struct_name {\n $(\n pub fn $field(mut self, value: $field_type) -> Self {\n self.$field = Some(value);\n self\n }\n )*\n\n pub fn build(self) -> Result<Self, String> {\n $(\n if self.$field.is_none() {\n return Err(format!("الحقل {} مطلوب", stringify!($field)));\n }\n )*\n Ok(self)\n }\n }\n };\n}\n\nbuilder!(Config, {\n host: String,\n port: u16,\n timeout: u64,\n});\n\nfn main() {\n let config = Config {\n host: None,\n port: None,\n timeout: None,\n }\n .host("localhost".to_string())\n .port(8080)\n .build()\n .unwrap();\n println!("التكوين جاهز: {}:{}", config.host.unwrap(), config.port.unwrap());\n}\n\n\nهذا الماكرو لا يولد الحقول فقط، بل يضيف أيضًا التحقق من الحقول الإجبارية. لاحظ stringify!($field) — فهو يحول المعرف إلى سلسلة نصية في مرحلة التجميع. هذه تقنية نموذجية من البرمجة الوصفية المتقدمة في Rust.
التكرارات: $(...)* في العمل
\n\nصيغة التكرار تسمح بمعالجة قوائم الوسائط بأي طول. من خلال دمج ident و ty و expr و "أجزاء" أخرى، يمكنك إنشاء أنماط معقدة. على سبيل المثال، ماكرو لإنشاء enum مع تنفيذ تلقائي لـ Display:
macro_rules! display_enum {\n ($enum_name:ident, { $($variant:ident => $desc:expr),* $(,)? }) => {\n enum $enum_name {\n $($variant),*\n }\n\n impl std::fmt::Display for $enum_name {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n match self {\n $(Self::$variant => write!(f, "{}", $desc)),*\n }\n }\n }\n };\n}\n\ndisplay_enum!(Status, {\n Active => "status_active",\n Inactive => "status_inactive",\n Pending => "s