البرمجة التفاعلية في Rust: مكتبات وأمثلة وتطبيقات عملية

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

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

ابدأ الدورة

البرمجة التفاعلية في Rust: من الكائنات القابلة للملاحظة إلى التدفقات غير المتزامنة

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

المكتبات الأساسية للبرمجة التفاعلية في Rust

لا يوجد "معيار" واحد للبرمجة التفاعلية في Rust، ولكن هناك عدة مكتبات ناضجة تغطي مستويات مختلفة من التجريد:

  • RxRust — نسخة من ReactiveX (Rx) الشهيرة في Rust. توفر Observable وObserver وScheduler ومجموعة واسعة من العوامل (map, filter, flat_map, merge وغيرها).
  • futures-rs — مكتبة أساسية للتعامل مع العمليات الحسابية غير المتزامنة. Stream trait هو أساس التدفقات التفاعلية.
  • async-std و tokio — بيئات تشغيل غير متزامنة توفر القنوات (channels) والمؤقتات وأدوات أخرى للسلاسل التفاعلية.
  • frunk — مكتبة للبرمجة الوظيفية تتضمن أنماطًا تفاعلية مثل Lens وState.

مثال 1: Observable بسيط مع RxRust

RxRust تنفذ نمط "المراقب": Observable يولد الأحداث، وObserver يشترك فيها.

// Cargo.toml: rxrust = "1.0"
use rxrust::prelude::*;

fn main() {
    let observable = observable::from_iter(vec![1, 2, 3, 4, 5]);

    observable
        .filter(|x| x % 2 == 0)
        .map(|x| x * 10)
        .subscribe(|x| println!("تم الاستلام: {}", x));
}

في هذا المثال، يتم إنشاء تدفق من الأرقام 1..5، ثم تصفية الأرقام الزوجية، وضربها في 10، ثم طباعتها. سلسلة العوامل تُنفذ بتكاسل — فقط بعد استدعاء subscribe.

مثال 2: التدفقات غير المتزامنة مع futures-rs

مكتبة futures-rs توفر trait Stream، وهو النظير غير المتزامن لـ Iterator. العوامل من StreamExt تسمح ببناء سلاسل تفاعلية.

// Cargo.toml: futures = "0.3"
use futures::stream::{self, StreamExt};

#[tokio::main]
async fn main() {
    let stream = stream::iter(0..10)
        .filter(|x| futures::future::ready(x % 2 == 0))
        .map(|x| x * 10)
        .take(3);

    let result: Vec<i32> = stream.collect().await;
    println!("النتيجة: {:?}", result); // [0, 20, 40]
}

هنا filter يقبل دالة غير متزامنة، مما يسمح بإدخال استدعاءات لواجهات برمجة التطبيقات الخارجية أو قواعد البيانات داخل السلسلة. take(3) يحدد عدد العناصر — وهذا مثال على "الضغط العكسي" (backpressure).

مثال 3: القنوات التفاعلية مع async-std

القنوات هي طريقة تقليدية لتنفيذ التفاعلات التفاعلية بين المكونات. async-std توفر قنوات متعددة المنتجين ومتعددة المستهلكين.

// Cargo.toml: async-std = "1.12"
use async_std::channel;
use async_std::task;

#[async_std::main]
async fn main() {
    let (sender, receiver) = channel::unbounded();

    // المنتج
    task::spawn(async move {
        for i in 0..5 {
            sender.send(i).await.unwrap();
            task::sleep(std::time::Duration::from_millis(100)).await;
        }
    });

    // المستهلك (تفاعلي)
    while let Some(msg) = receiver.recv().await {
        println!("تم الاستلام: {}", msg);
        if msg == 4 {
            break;
        }
    }
}

القنوات مثالية لتنفيذ Event Bus أو ناقل الأحداث أو أنظمة تفاعلية بسيطة مع مصادر بيانات متعددة.

الأنماط المتقدمة: دمج التدفقات

تتجلى قوة البرمجة التفاعلية عند دمج مصادر بيانات متعددة. يمكنك استخدام عوامل مثل merge وzip وcombine_latest لإنشاء تدفقات جديدة من تدفقات موجودة. هذا مفيد بشكل خاص في تطبيقات الواجهة الأمامية وأنظمة إنترنت الأشياء ومعالجة البيانات في الوقت الفعلي.

توصيات