Rust يقتحم عالم الأنظمة المضمنة: لماذا لم يعد C هو الملك

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

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

ابدأ الدورة

مقدمة: عصر "العتاد" يغير اللغة

\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.

\n\n

تخيل أنك تكتب مشغلًا (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

تكلفة صفرية للتجريدات

\n\n

أحد المخاوف الرئيسية لمطوري الأنظمة المضمنة هو أن اللغة عالية المستوى سوف "تستهلك" دورات المعالج. تتبع Rust مبدأ zero-cost abstractions: أنت تدفع فقط مقابل ما تستخدمه. المكررات (iterators)، والإغلاقات (closures)، والسمات (traits) — كل هذا يتم ترجمته إلى كود آلة بنفس كفاءة فك الحلقات يدويًا بلغة C.

\n\n

علاوة على ذلك، في النظام البيئي للأنظمة المضمنة في Rust، هناك كريتات (crates) تعمل على مستوى التعليمات الفردية. على سبيل المثال، cortex-m-rt يولد متجه المقاطعات وكود بدء التشغيل بدون تعليمة واحدة زائدة.

\n\n

مشروع حقيقي: إضاءة LED باستخدام Rust (STM32)

\n\n

دعنا نكتب برنامجًا بسيطًا للوحة STM32F3Discovery. سنستخدم cortex-m-quickstart و HAL من مجتمع embedded rust.

\n\n

الخطوة 1: إعداد المشروع

\n\n

تأكد من تثبيت Rust وسلسلة أدوات ARM Cortex-M:

\n\n
rustup target add thumbv7em-none-eabihf\ncargo install cargo-generate
\n\n

ننشئ مشروعًا من القالب:

\n\n
cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
\n\n

الخطوة 2: كتابة الكود

\n\n

نفتح src/main.rs ونكتب برنامج Blinky الكلاسيكي:

\n\n
#![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] — نحن ن

المدونات

توصيات الكتب