使用 WebAssembly 和 Rust 进行开发:高级 Web 集成方法

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为何需要高级水平的 Rust 和 WebAssembly

\n

WebAssembly (Wasm) 和 Rust 的结合,可以将计算密集型任务从 JavaScript 迁移到在浏览器中运行的高性能原生代码。在基础层面上,您可能已经了解了简单函数的编译。然而,要创建真正的产品——游戏引擎、图像处理器、解析器或加密库——需要深入理解内存管理、复杂数据结构的传递以及与外部环境的交互。

\n

在本文中,我们将探讨使用 Rust 进行 WebAssembly 开发的高级技术:从手动内存管理到创建 Rust 和 JavaScript 协同工作的混合应用程序。本文面向已经熟悉 Rust 和 Wasm 基础的开发者。

\n\n

1. 内存管理:线性内存与分配器

\n

与 JavaScript 不同,WebAssembly 使用线性内存——一个扁平的字节数组。编译为 Wasm 的 Rust 代码直接操作此内存。为了高效工作,需要理解如何手动分配和释放内存。

\n\n

1.1. 使用 wasm_bindgen 手动分配内存

\n

wasm-bindgen 库提供了便捷的抽象,但在高级水平上,通常需要直接访问内存。请看以下示例:

\n
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 函数恢复向量并正确释放内存。

\n\n

1.2. 使用自定义分配器

\n

对于复杂项目,建议将标准分配器替换为更可预测的分配器。例如,wee_alloc 是一个针对 Wasm 优化的小型分配器:

\n
// 在 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\n

2. 在 Rust 和 JavaScript 之间传递复杂数据结构

\n

在高级水平上,您不再局限于数字和字符串。需要传递数组、结构体甚至闭包。

\n\n

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

博客

书籍推荐