مقدمة: عصر "العتاد" يغير اللغة
\n\nإذا سبق لك كتابة برنامج ثابت (firmware) لمتحكم دقيق بلغة C، فأنت تعرف هذا الشعور. الشعور عندما تجد بعد ثلاث ساعات من التصحيح خطأ معلقًا — مؤشر طار في السماء، و watchdog تحمل كل شيء. لسنوات طويلة، كان تطوير الأنظمة المضمنة مجالًا خاصًا بلغة C ولغة التجميع. ولكن في العامين الماضيين، ظهر لاعب جديد على الساحة — Rust. وهي ليست مجرد "لغة أخرى"، بل هي نقلة نوعية حقيقية للأنظمة المضمنة.
\n\nفي هذه المقالة، سنشرح لماذا تطوير الأنظمة المضمنة باستخدام Rust ليس مجرد ضجة، بل ضرورة. سنلقي نظرة على أمثلة حقيقية للكود، ونقارنها بلغة C، ونفهم من أين نبدأ. إذا كنت تكتب كودًا لـ STM32 أو ESP32 أو ARM Cortex-M، فهذا المقال لك.
\n\nلماذا Rust للأنظمة المضمنة هو زواج مثالي
\n\nالأنظمة المضمنة هي عالم من القيود. ذاكرة قليلة، لا يوجد نظام تشغيل، لا يوجد كومة (heap)، لا توجد استثناءات. خطأ في الترجمة جيد، خطأ في وقت التشغيل سيء. صُممت Rust خصيصًا لمثل هذه الظروف.
\n\nأمان الذاكرة بدون جامع القمامة
\n\nالألم الرئيسي لمطور الأنظمة المضمنة بلغة C هو use-after-free، والتحرير المزدوج للذاكرة، وتجاوز حدود المخزن المؤقت. تحل Rust هذه المشكلات على مستوى الأنواع. نموذج الملكية (ownership) والاقتراض (borrowing) يعمل حتى في بيئة no_std — بدون مخصص ذاكرة، بدون نظام تشغيل، بدون GC.
تخيل أنك تكتب مشغلًا (driver) لـ LED. في لغة C ستكتب:
\n\n// C code\nvoid led_on(GPIO_TypeDef *gpio, uint16_t pin) {\n gpio->BSRR = pin;\n}\n\nكل شيء جيد، حتى يقوم شخص بتمرير مؤشر فارغ. Rust لن تسمح لك بذلك:
\n\n// Rust code\nfn led_on(gpio: &mut GpioRegisters, pin: u16) {\n gpio.bsrr.write(|w| w.bits(pin.into()));\n}\n\nهنا &mut GpioRegisters هو ضمان أن المرجع صالح والوصول إلى المسجلات حصري. المترجم ببساطة لن يمرر الكود الخطير.
تكلفة صفرية للتجريدات
\n\nأحد المخاوف الرئيسية لمطوري الأنظمة المضمنة هو أن اللغة عالية المستوى سوف "تستهلك" دورات المعالج. تتبع Rust مبدأ zero-cost abstractions: أنت تدفع فقط مقابل ما تستخدمه. المكررات (iterators)، والإغلاقات (closures)، والسمات (traits) — كل هذا يتم ترجمته إلى كود آلة بنفس كفاءة فك الحلقات يدويًا بلغة C.
\n\nعلاوة على ذلك، في النظام البيئي للأنظمة المضمنة في Rust، هناك كريتات (crates) تعمل على مستوى التعليمات الفردية. على سبيل المثال، cortex-m-rt يولد متجه المقاطعات وكود بدء التشغيل بدون تعليمة واحدة زائدة.
مشروع حقيقي: إضاءة LED باستخدام Rust (STM32)
\n\nدعنا نكتب برنامجًا بسيطًا للوحة STM32F3Discovery. سنستخدم cortex-m-quickstart و HAL من مجتمع embedded rust.
الخطوة 1: إعداد المشروع
\n\nتأكد من تثبيت Rust وسلسلة أدوات ARM Cortex-M:
\n\nrustup target add thumbv7em-none-eabihf\ncargo install cargo-generate\n\nننشئ مشروعًا من القالب:
\n\ncargo generate --git https://github.com/rust-embedded/cortex-m-quickstart\n\nالخطوة 2: كتابة الكود
\n\nنفتح src/main.rs ونكتب برنامج Blinky الكلاسيكي:
#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse panic_halt as _;\nuse stm32f3xx_hal::{pac, prelude::*};\n\n#[entry]\nfn main() -> ! {\n // الحصول على الوصول إلى الأجهزة الطرفية\n let dp = pac::Peripherals::take().unwrap();\n let mut rcc = dp.RCC.constrain();\n let mut gpioe = dp.GPIOE.split(&mut rcc.ahb);\n\n // تكوين الدبوس PE9 كمخرج (LED LD1)\n let mut led = gpioe\n .pe9\n .into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper);\n\n loop {\n led.set_high().unwrap();\n cortex_m::asm::delay(8_000_000);\n led.set_low().unwrap();\n cortex_m::asm::delay(8_000_000);\n }\n}\n\nماذا يحدث هنا؟
\n- \n
#![no_std]— نحن ن