DSL بلغة Rust: كتابة لغة خاصة بنا للمهام الواقعية

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

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

ابدأ الدورة

لماذا يحتاج مطور Rust إلى لغة خاصة به؟ انغمس في عالم DSL

\n\n

تخيل أنك تكتب محللاً لملف إعدادات معقد، أو تنفذ منطق لعبة، أو تصف قواعد عمل. في كل مرة تصطدم بنفس المشكلة: يصبح الكود ضخماً، وتقل قابلية القراءة. ماذا لو كان بإمكانك إنشاء لغة مصغرة مصممة خصيصاً لمهمتك؟ هذا هو اللغة الموجهة للمجال (DSL بلغة Rust).

\n\n

Rust هي البيئة المثالية لإنشاء DSL. نظام وحدات الماكرو الخاص بها، واستنتاج الأنواع القوي، والتكلفة الصفرية للتجريدات تسمح ببناء لغات لا تقل أداءً عن الكود المكتوب يدوياً. في هذه المقالة، سنشرح كيفية تحويل Rust إلى أداة بناء للغاتك الخاصة، ونوضح لماذا قد يكون هذا مفيداً لك تحديداً.

\n\n

ما هو DSL ولماذا هو مطلوب في Rust؟

\n\n

DSL (Domain-Specific Language) هي لغة تم إنشاؤها لحل مشاكل في مجال معين. على عكس لغة البرمجة العامة، تضحي DSL بالعمومية من أجل التعبيرية في مجال ضيق. في نظام Rust البيئي، يوجد نهجان رئيسيان:

\n\n
    \n
  • DSL داخلي (مدمج) — يُبنى فوق بناء جملة Rust باستخدام وحدات الماكرو وسلاسل الدوال. مثال: مكتبة clap لتحليل وسائط سطر الأوامر.
  • \n
  • DSL خارجي — بناء جملة منفصل يتم تحليله في وقت التشغيل. مثال: regex أو sqlx.
  • \n
\n\n

تطوير DSL بلغة Rust مهم بشكل خاص لـ:

\n
    \n
  • ملفات الإعدادات (YAML/TOML كـ DSL خارجي)
  • \n
  • محركات الألعاب (وصف سلوك الشخصيات غير القابلة للعب)
  • \n
  • أطر الاختبار (بناء جملة خاص للسيناريوهات)
  • \n
  • الموازنة بين الأمان والمرونة
  • \n
\n\n

إنشاء DSL داخلي بلغة Rust: وحدات الماكرو والسلاسل

\n\n

لنبدأ بشيء بسيط — DSL داخلي لوصف طلبات HTTP. بدلاً من كتابة:

\n\n
let mut req = Request::new(Method::GET);\nreq.set_url("https://api.example.com/data");\nreq.add_header("Authorization", "Bearer token");\nreq.add_header("Content-Type", "application/json");\n
\n\n

نريد الحصول على بناء جملة موجز:

\n\n
let req = request! {\n    GET "https://api.example.com/data"\n    headers {\n        "Authorization": "Bearer token",\n        "Content-Type": "application/json"\n    }\n};\n
\n\n

سنقوم بتنفيذ ذلك باستخدام وحدات الماكرو في Rust — أقوى أداة لإنشاء DSL داخلي:

\n\n
macro_rules! request {\n    ($method:ident $url:expr) => {\n        Request::new(stringify!($method).parse().unwrap(), $url)\n    };\n    ($method:ident $url:expr, headers { $($key:expr => $value:expr),* $(,)? }) => {\n        {\n            let mut req = Request::new(stringify!($method).parse().unwrap(), $url);\n            $(\n                req.add_header($key, $value);\n            )*\n            req\n        }\n    };\n}\n
\n\n

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

\n\n

DSL داخلي متقدم: نمط البناء (Builder pattern)

\n\n

وحدات الماكرو ليست الطريقة الوحيدة. غالباً ما يُستخدم نمط Builder مع سلاسل الدوال لإنشاء DSL في Rust. لنأخذ مثالاً على DSL لاستعلامات SQL:

\n\n
let query = Query::select()\n    .from("users")\n    .where_clause("age > ?")\n    .bind(18)\n    .order_by("name")\n    .build();\n
\n\n

تنفيذ مثل هذا DSL بسيط وأنيق:

\n\n
struct QueryBuilder {\n    select: Vec<String>,\n    from: String,\n    condition: Option<String>,\n    order: Option<String>,\n}\n                

المدونات

توصيات الكتب