`wasm-bindgen` 的设计

本节旨在深入探讨 `wasm-bindgen` 在 Rust 中的内部工作原理,特别是针对 Rust。如果您在未来很长时间后阅读本文,它可能不再是最新的,但请随时打开一个问题,我们会尝试回答您的问题或更新本文!

基础:ES 模块

关于 `wasm-bindgen` 的第一件事是,它从根本上建立在 ES 模块的概念之上。换句话说,这个工具认为 wasm 文件 *应该被视为 ES 模块*。这意味着您可以从正常的 JS 文件中 `import` wasm 文件,使用其 `export` 的功能等等。

不幸的是,在撰写本文时,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 以及您的工具链,您就可以立即获得对组件模型的原始访问权限!(不过这还需要一段时间...)