wasm-bindgen 的設計

本節旨在深入探討 wasm-bindgen 目前的內部運作方式,特別是針對 Rust。如果您在未來閱讀本文,它可能不再是最新的,但歡迎提出問題,我們會盡力回答問題和/或更新本文!

基礎: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_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 以及您的工具鏈,就可以立即獲得對元件模型的原始存取權!(不過這還需要一段時間...)