مقدمة: لماذا Rust وWebAssembly على المستوى المتقدم
\nWebAssembly (Wasm) وRust هما مزيج يسمح بنقل المهام الحسابية الثقيلة من JavaScript إلى كود أصلي عالي الأداء يعمل في المتصفح. على المستوى الأساسي، قد تكون قد تعرفت بالفعل على ترجمة الدوال البسيطة. ومع ذلك، لإنشاء منتجات حقيقية - مثل محركات الألعاب، معالجات الصور، المحللات، أو المكتبات التشفيرية - يتطلب الأمر فهماً عميقاً لإدارة الذاكرة، نقل هياكل البيانات المعقدة، والتفاعل مع البيئة الخارجية.
\nفي هذه المقالة، سنستعرض التقنيات المتقدمة للعمل مع WebAssembly باستخدام Rust: من الإدارة اليدوية للذاكرة إلى إنشاء تطبيقات هجينة حيث يعمل Rust وJavaScript ككيان واحد. المادة موجهة للمطورين الذين لديهم بالفعل معرفة بأساسيات Rust وWasm.
\n\n1. إدارة الذاكرة: الذاكرة الخطية والمخصصات
\nعلى عكس JavaScript، يستخدم WebAssembly ذاكرة خطية - مصفوفة مسطحة من البايتات. يعمل كود Rust المترجم إلى Wasm مع هذه الذاكرة بشكل مباشر. للعمل بكفاءة، من الضروري فهم كيفية تخصيص وتحرير الذاكرة يدوياً.
\n\n1.1. تخصيص الذاكرة يدوياً باستخدام wasm_bindgen
\nتوفر مكتبة wasm-bindgen تجريدات ملائمة، ولكن على المستوى المتقدم غالباً ما يكون الوصول المباشر إلى الذاكرة مطلوباً. لنلق نظرة على مثال:
use wasm_bindgen::prelude::*;\nuse std::mem;\nuse std::slice;\n\n#[wasm_bindgen]\npub fn allocate_buffer(size: usize) -> *mut u8 {\n let mut buf = Vec::with_capacity(size);\n let ptr = buf.as_mut_ptr();\n mem::forget(buf); // يمنع حذف المتجه\n ptr\n}\n\n#[wasm_bindgen]\npub fn deallocate_buffer(ptr: *mut u8, size: usize) {\n unsafe {\n let _ = Vec::from_raw_parts(ptr, 0, size);\n }\n}\n\n#[wasm_bindgen]\npub fn fill_buffer(ptr: *mut u8, value: u8, len: usize) {\n let slice = unsafe { slice::from_raw_parts_mut(ptr, len) };\n for byte in slice.iter_mut() {\n *byte = value;\n }\n}\nهنا نقوم بتخصيص الذاكرة عبر Vec، و"ننسى" أمره (لمنع Rust من حذف المخزن المؤقت) ونمرر المؤشر إلى JavaScript. تقوم الدالة deallocate_buffer باستعادة المتجه وتحرير الذاكرة بشكل صحيح.
1.2. استخدام مخصص مخصص للمستخدم
\nللمشاريع المعقدة، يُفضل استبدال المخصص الافتراضي بآخر أكثر قابلية للتنبؤ. على سبيل المثال، wee_alloc هو مخصص صغير محسّن لـ Wasm:
// في Cargo.toml\n// [dependencies]\n// wee_alloc = "0.4"\n\n#[global_allocator]\nstatic ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;\nسيؤدي هذا إلى تقليل حجم ملف .wasm النهائي وتسريع تخصيص الذاكرة في المتصفح.
\n\n2. نقل هياكل البيانات المعقدة بين Rust وJavaScript
\nعلى المستوى المتقدم، لا تقتصر على الأرقام والسلاسل النصية. من الضروري نقل المصفوفات، الهياكل، وحتى الإغلاقات.
\n\n2.1. الهياكل مع #[wasm_bindgen]
\nيمكنك تصدير هياكل Rust كاملة كفئات JavaScript:
\n#[wasm_bindgen]\npub struct Matrix {\n rows: usize,\n cols: usize,\n data: Vec<f64>,\n}\n\n#[wasm_bindgen]\nimpl Matrix {\n #[wasm_bindgen(constructor)]\n pub fn new(rows: usize, cols: usize) -> Matrix {\n Matrix {\n rows,\n cols,\n data: vec![0.0; rows * cols],\n }\n }\n\n pub fn get(&self, row: usize, col: usize) -> f64 {\n self.data[row * self.cols + col]\n }\n\n pub fn set(&mut self, row: usize, col: usize, val: f64) {\n self.data[row * self.cols + col] = val;\n }\n\n pub fn multiply(&self, other: &Matrix) -> Result<Matrix, String> {\n if self.cols != other.rows {\n return Err("أبعاد غير متوافقة".to_string());\n }\n // ... تنفيذ ضرب المصفوفات\n let result = Matrix::new(self.rows, other.cols);\n Ok(result)\n