مقدمة في البرمجة غير المتزامنة في Rust
تتطلب التطبيقات الحديثة بشكل متزايد أداءً عاليًا عند العمل مع الشبكة أو نظام الملفات أو قواعد البيانات. تتيح البرمجة غير المتزامنة في Rust معالجة آلاف الاتصالات المتزامنة بكفاءة دون إنشاء خيوط لكل مهمة. على عكس تعدد الخيوط، يستخدم الكود غير المتزامن ذاكرة أقل ويتوسع بشكل أفضل.
تقدم Rust نهجًا فريدًا للبرمجة غير المتزامنة يعتمد على التجريدات ذات التكلفة الصفرية. هذا يعني أنك تدفع فقط مقابل ما تستخدمه - دون أي تكاليف إضافية للوظائف غير المستخدمة. في هذا الدليل، سنشرح المفاهيم الأساسية ونكتب أمثلة عملية ستساعدك على البدء في كتابة كود غير متزامن في Rust اليوم.
أساسيات async/await وFutures
ما هو Future؟
Future هو trait يمثل عملية غير متزامنة قد تكتمل في المستقبل. في Rust، Future هو كيان كسول: لا يفعل شيئًا حتى يتم استطلاعه (poll). الفكرة الأساسية: بدلاً من حظر الخيط، نقوم بتسجيل مهمة ونحصل على النتيجة عندما تكون جاهزة.
use std::future::Future;use std::pin::Pin;use std::task::{Context, Poll};
struct MyFuture { ready: bool,}
impl Future for MyFuture { type Output = u32;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<u32> { if self.ready { Poll::Ready(42) } else { // تسجيل waker لإعادة الاستطلاع cx.waker().wake_by_ref(); Poll::Pending } }}async/await - سكر نحوي
الكلمة المفتاحية async تحول الدالة أو الكتلة إلى Future. await توقف التنفيذ حتى اكتمال Future، وتعيد التحكم إلى بيئة التشغيل.
use tokio::time::{sleep, Duration};
async fn fetch_data() -> String { // محاكاة طلب شبكة sleep(Duration::from_secs(2)).await; String::from("تم استلام البيانات")}
#[tokio::main]async fn main() { let result = fetch_data().await; println!("{}", result);}دورات الحياة ومدقق الاقتراض (borrow checker)
إحدى ميزات Rust هي التحكم الصارم في الاقتراض. في الكود غير المتزامن، هذا مهم بشكل خاص لأن Future قد يعيش لفترة أطول من البيانات المقترضة. استخدم async move لالتقاط الملكية:
async fn process_data(data: Vec<u32>) -> u32 { data.iter().sum()}
#[tokio::main]async fn main() { let numbers = vec![1, 2, 3, 4, 5]; let sum = process_data(numbers).await; println!("المجموع: {}", sum);}العمل العملي مع Tokio
التثبيت والإعداد
Tokio هو بيئة التشغيل غير المتزامنة الأكثر شيوعًا لـ Rust. أضف إلى Cargo.toml:
[dependencies]tokio = { version = "1", features = ["full"] }إنشاء خادم TCP غير متزامن
لننظر إلى خادم صدى بسيط يستقبل الاتصالات ويرسل البيانات المستلمة مرة أخرى:
use tokio::net::TcpListener;use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let listener = TcpListener::bind("127.0.0.1:8080").await?; println!("الخادم يعمل على المنفذ 8080");
loop { let (mut socket, addr) = listener.accept().await?; println!("اتصال جديد من: {}", addr);
tokio::spawn(async move { let mut buf = [0; 1024]; loop { let n = socket.read(&mut buf).await.unwrap(); if n == 0 { break; } // تم إغلاق الاتصال socket.write_all(&buf[..n]).await.unwra