wasm-bindgen
的设计
本节旨在深入探讨 `wasm-bindgen` 当前在内部如何工作,特别是针对 Rust。如果您在遥远的未来阅读本文,它可能不再是最新的,但请随时打开一个 issue,我们可以尝试回答问题和/或更新本文!
基础:ES 模块
关于 `wasm-bindgen` 首先要知道的是,它从根本上建立在 ES 模块的思想之上。换句话说,此工具采取了一种固执己见的立场,即 Wasm 文件 *应被视为 ES 模块*。这意味着您可以从 Wasm 文件 `import`,使用其 `export` 的功能等,从普通的 JS 文件中。
现在,不幸的是,在撰写本文时,Wasm 互操作的接口不是很丰富。Wasm 模块只能调用或导出专门处理 `i32`、`i64`、`f32` 和 `f64` 的函数。真可惜!
这就是这个项目的用武之地。`wasm-bindgen` 的目标是使用更丰富的类型(如类、JS 对象、Rust 结构体、字符串等)来增强 Wasm 模块的“ABI”。但请记住,一切都基于 ES 模块!这意味着编译器实际上正在生成某种“损坏”的 Wasm 文件。例如,rustc 发出的 wasm 文件不具有我们希望拥有的接口。相反,它需要 `wasm-bindgen` 工具来后处理该文件,生成 `foo.js` 和 `foo_bg.wasm` 文件。`foo.js` 文件是以 JS 表示的所需接口(类、类型、字符串等),而 `foo_bg.wasm` 模块仅用作实现细节(它与原始的 `foo.wasm` 文件略有不同)。
随着时间的推移,WebAssembly 中稳定了更多功能(如组件模型),JS 文件预计会越来越小。它不太可能消失,但 `wasm-bindgen` 的设计宗旨是密切遵循 WebAssembly 规范和提案,以尽可能优化 JS/Rust。
基础 2:在 Rust 中不显眼
在 Rust 方面,`wasm-bindgen` crate 的设计理念是尽可能减少对 Rust crate 的影响。理想情况下,只需在关键位置注解一些 `#[wasm_bindgen]` 属性,其余的事情就可以顺利进行。该属性力求不发明新的语法,并与今天现有的习语一起使用。
例如,一个库可能会在普通的 Rust 中暴露一个如下所示的函数
#![allow(unused)] fn main() { pub fn greet(name: &str) -> String { // ... } }
使用 `#[wasm_bindgen]` 将其导出到 JS 所需做的全部工作是
#![allow(unused)] fn main() { #[wasm_bindgen] pub fn greet(name: &str) -> String { // ... } }
此外,这里的设计对 Rust 的干预最小,这应该可以让我们轻松利用即将推出的 组件模型 提案。理想情况下,您只需升级 `wasm-bindgen`-the-crate 以及您的工具链,您就可以立即获得对组件模型的原始访问权限!(但这仍然需要一段时间...)