這份文件是關於 Rust 與 WebAssembly 處理尚未發表的,已發表的文章可在 Rust 與 WebAssembly 文件主要網站 取得。在此說明的功能可能在 Rust 與 WebAssembly 工具發行的版本中無法使用。

如何為通用用途箱子添加 WebAssembly 支援

此章節適用於想要支援 WebAssembly 的通用用途箱子作者。

你的箱子可能已經支援 WebAssembly!

檢閱關於 那些可能讓通用用途箱子適用於 WebAssembly 的事項。如果你的箱子裡沒有那些事項,它可能已經支援 WebAssembly!

你可以隨時執行 WebAssembly 目標的 cargo build 來確認

cargo build --target wasm32-unknown-unknown

如果指令執行失敗,就表示你的箱子現在不支援 WebAssembly。如果執行成功,你的箱子可能支援 WebAssembly。你可以透過新增 wasm 測試並在 CI 中執行測試來確切確認箱子支援 WebAssembly(而且一直持續支援)

加入 WebAssembly 支援

避免直接執行 I/O

在 Web 上,I/O 永遠都是非同步的,那裡也沒有檔案系統。將 I/O 從你的函式庫中抽離,讓使用者執行 I/O,然後將輸入切片傳遞給你的函式庫。

例如,重新整理以下程式碼段


# #![allow(unused_variables)]
#fn main() {
use std::fs;
use std::path::Path;

pub fn parse_thing(path: &Path) -> Result<MyThing, MyError> {
    let contents = fs::read(path)?;
    // ...
}
#}

成為以下程式碼段


# #![allow(unused_variables)]
#fn main() {
pub fn parse_thing(contents: &[u8]) -> Result<MyThing, MyError> {
    // ...
}
#}

新增 `wasm-bindgen` 作為依賴項

如果你需要與外部世界互動(即無法請函式庫消費者為你驅動這個互動),則需要新增 `wasm-bindgen`(以及 `js-sys` 和 `web-sys`(如果你需要的話))作為依賴項,以便在編譯目標為 WebAssembly 時使用

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = "0.3"

避免同步 I/O

如果你必須在你的函式庫中執行 I/O,則它不能同步進行。在 Web 上只有非同步 I/O。使用 `futures` 板條箱`wasm-bindgen-futures` 板條箱 來管理非同步 I/O。如果你的函式庫函式是泛型,且帶有一些 future 類型 `F`,則該 future 可以透過 Web 上的 `fetch` 或作業系統提供的非封鎖 I/O 實作。


# #![allow(unused_variables)]
#fn main() {
pub fn do_stuff<F>(future: F) -> impl Future<Item = MyOtherThing>
where
    F: Future<Item = MyThing>,
{
    // ...
}
#}

你也可以定義一個特質,並為 WebAssembly、Web 和原生目標實作它


# #![allow(unused_variables)]
#fn main() {
trait ReadMyThing {
    type F: Future<Item = MyThing>;
    fn read(&self) -> Self::F;
}

#[cfg(target_arch = "wasm32")]
struct WebReadMyThing {
    // ...
}

#[cfg(target_arch = "wasm32")]
impl ReadMyThing for WebReadMyThing {
    // ...
}

#[cfg(not(target_arch = "wasm32"))]
struct NativeReadMyThing {
    // ...
}

#[cfg(not(target_arch = "wasm32"))]
impl ReadMyThing for NativeReadMyThing {
    // ...
}
#}

避免產生執行緒

Wasm 尚未支援執行緒(但 實驗工作正在進行中),因此嘗試在 wasm 中產生執行緒會引發恐慌。

你可以使用 `#[cfg(..)]` 依照目標是 WebAssembly 或不是來啟用有執行緒和沒有執行緒的程式碼路徑


# #![allow(unused_variables)]
#![cfg(target_arch = "wasm32")]
#fn main() {
fn do_work() {
    // Do work with only this thread...
}

#![cfg(not(target_arch = "wasm32"))]
fn do_work() {
    use std::thread;

    // Spread work to helper threads....
    thread::spawn(|| {
        // ...
    });
}
#}

另一個選擇是從函式庫中抽離產生執行緒的步驟,並允許使用者「自行處理執行緒」,類似於抽離檔案 I/O 並允許使用者自行處理 I/O。這會產生一個優點,即能與想要擁有自己的客製化執行緒池的應用程式相處融洽。

維持對 WebAssembly 的持續支援

針對 CI 中的 wasm32-unknown-unknown 進行建置

透過執行這些命令讓 CI 腳本針對 WebAssembly 的編譯不會失敗

rustup target add wasm32-unknown-unknown
cargo check --target wasm32-unknown-unknown

例如,你可以將以下程式碼新增到你的 Travis CI 的 ` .travis.yml ` 組態中


matrix:
  include:
    - language: rust
      rust: stable
      name: "check wasm32 support"
      install: rustup target add wasm32-unknown-unknown
      script: cargo check --target wasm32-unknown-unknown

在 Node.js 和無頭瀏覽器中測試

你可以使用 `wasm-bindgen-test` 和 `wasm-pack test` 子命令在 Node.js 或無頭瀏覽器中執行 wasm 測試。你甚至可以將這些測試整合到你的 CI 中。

在此深入了解如何測試 wasm。