wasm-bindgen 的设计

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

基础:ES 模块

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

现在不幸的是,在撰写本文时,wasm 交互的接口并不十分丰富。Wasm 模块只能调用函数或导出仅处理i32i64f32f64 的函数。太糟糕了!

这就是这个项目发挥作用的地方。wasm-bindgen 的目标是使用更丰富的类型(如类、JS 对象、Rust 结构体、字符串等)来增强 wasm 模块的“ABI”。请记住,所有内容都基于 ES 模块!这意味着编译器实际上正在生成一个“损坏”的 wasm 文件。例如,由 rustc 发出的 wasm 文件没有我们想要的接口。相反,它需要 wasm-bindgen 工具来后处理该文件,生成一个 foo.jsfoo_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_variables)]
#fn main() {
pub fn greet(name: &str) -> String {
    // ...
}
#}

使用 #[wasm_bindgen],您只需要将其导出到 JS 中


# #![allow(unused_variables)]
#fn main() {
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    // ...
}
#}

此外,这种设计在 Rust 中的最小干预应该让我们能够轻松地利用即将推出的组件模型提案。理想情况下,您只需升级 wasm-bindgen-the-crate 以及您的工具链,您就可以立即获得对组件模型的原始访问权限!(不过这还需要一段时间……)