引言:为何需要高级水平的 Rust 和 WebAssembly
\nWebAssembly (Wasm) 和 Rust 的结合,可以将计算密集型任务从 JavaScript 迁移到在浏览器中运行的高性能原生代码。在基础层面上,您可能已经了解了简单函数的编译。然而,要创建真正的产品——游戏引擎、图像处理器、解析器或加密库——需要深入理解内存管理、复杂数据结构的传递以及与外部环境的交互。
\n在本文中,我们将探讨使用 Rust 进行 WebAssembly 开发的高级技术:从手动内存管理到创建 Rust 和 JavaScript 协同工作的混合应用程序。本文面向已经熟悉 Rust 和 Wasm 基础的开发者。
\n\n1. 内存管理:线性内存与分配器
\n与 JavaScript 不同,WebAssembly 使用线性内存——一个扁平的字节数组。编译为 Wasm 的 Rust 代码直接操作此内存。为了高效工作,需要理解如何手动分配和释放内存。
\n\n1.1. 使用 wasm_bindgen 手动分配内存
\nwasm-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