這是 Rust 和 WebAssembly 工作的文件未發佈,已發佈的文件可以在 Rust 和 WebAssembly 主要文件網站 找到。在此記錄的功能在 Rust 和 WebAssembly 的工具已發佈版本中可能無法使用。

JavaScript 互通

匯入和匯出 JS 函式

從 Rust 端

在 JS 主機內使用 wasm 時,從 Rust 端匯入和匯出函式非常簡單:運作方式與 C 非常類似。

WebAssembly 模組宣告一系列的匯入,每個匯入具有模組名稱匯入名稱。可以使用 #[link(wasm_import_module)] 指定 extern { ... } 區塊的模組名稱,目前預設為 "env"。

匯出只有一個名稱。除了任何 extern 函式外,WebAssembly 執行個體的預設線性記憶體會以 "memory" 匯出。


# #![allow(unused_variables)]
#fn main() {
// import a JS function called `foo` from the module `mod`
#[link(wasm_import_module = "mod")]
extern { fn foo(); }

// export a Rust function called `bar`
#[no_mangle]
pub extern fn bar() { /* ... */ }
#}

由於 wasm 的值類型有限,這些函式只能對基本數字類型運作。

從 JS 端

在 JS 中,wasm 二進制碼會變為 ES6 模組。它必須實例化,並具有線性記憶體,以及與預期輸入相符的 JS 函數組。實例化詳情可在 MDN 上取得。

產生的 ES6 模組將包含所有從 Rust 匯出的函數,現在可以使用 JS 函數了。

這裡是一個完整的設定範例,非常簡單。

超越數值

在 JS 中使用 wasm 時,wasm 模組的記憶體與 JS 記憶體之間有明顯的區別

  • 每個 wasm 模組都有線性記憶體(在本文檔開頭說明),是在實例化期間初始化。JS 程式碼可以任意讀取和寫入此記憶體

  • 相反地,wasm 程式碼沒有直接存取 JS 物件。

因此,精細的互動以兩種主要方式發生

  • 複製進出二進制資料到 wasm 記憶體。例如,這是提供一個已擁有 字串 給 Rust 端的一種方式。

  • 設定一個 JS 物件的明確「堆疊」,然後給予其「位址」。這允許 wasm 程式碼間接地參照 JS 物件(使用整數),並透過呼叫輸入的 JS 函數來對這些物件進行操作。

很幸運地,這種互動在一個通用「bindgen」風格架構中,很適合處理:wasm-bindgen。此架構可以自動編寫與慣用的 JS 函數對應的慣用 Rust 函數簽章。

自訂區塊

自訂區塊允許嵌入具名稱的任意資料到 wasm 模組中。區塊資料是在編譯期間設置,並直接從 wasm 模組中讀取,無法在執行階段修改。

在 Rust 中,自訂區塊是透過 #[link_section] 屬性公開的靜態陣列([T; 大小]


# #![allow(unused_variables)]
#fn main() {
#[link_section = "hello"]
pub static SECTION: [u8; 24] = *b"This is a custom section";
#}

這會將一個名為 hello 的自訂區塊新增到 wasm 檔案,Rust 變數名稱 SECTION 是隨意的,更改並不會改變行為。內容在這裡是文字的位元組,但可以是任何任意資料。

可以在 JS 端使用 WebAssembly.Module.customSections 函數讀取自訂區塊,它接收一個 wasm 模組和區塊名稱作為參數,並傳回一個 ArrayBuffer 陣列。可以使用同一個名稱指定多個區塊,這種情況下它們都會出現在此陣列中。

WebAssembly.compileStreaming(fetch("sections.wasm"))
.then(mod => {
  const sections = WebAssembly.Module.customSections(mod, "hello");

  const decoder = new TextDecoder();
  const text = decoder.decode(sections[0]);

  console.log(text); // -> "This is a custom section"
});