如何為通用專用容器加入 WebAssembly 支援

本節適用於想要支援 WebAssembly 的通用專用容器作者。

您的專用容器可能已支援 WebAssembly!

檢閱有關資訊 哪些情況可能會讓通用專用容器無法移植至 WebAssembly。如果您的專用容器沒有這些情況,很有可能已支援 WebAssembly!

您隨時可以執行 cargo build 來檢查 WebAssembly 目標

cargo build --target wasm32-unknown-unknown

如果指令失敗,表示您的專用容器目前不支援 WebAssembly。如果沒有失敗,表示您的專用容器可能支援 WebAssembly。您可以 新增對 wasm 的測試,並在 CI 中執行這些測試,以 100% 確定專用容器確實支援 WebAssembly(並持續支援中)。

加入對 WebAssembly 的支援

避免直接執行 I/O

在網路上,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 新增為相依性

如果您需要與外界互動(即您不能讓函式庫消費者為您驅動該互動),則當編譯目標為 WebAssembly 時,您需要新增 wasm-bindgen(如果需要,還要新增 js-sysweb-sys)作為相依性。

[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 cratewasm-bindgen-futures crate 來管理非同步 I/O。如果您的函式庫函數針對某些未來類型 F 為通用,則可以在 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 的持續支援

針對 wasm32-unknown-unknown 在 CI 中建置

透過讓您的 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-testwasm-pack test 子指令在 Node.js 或無頭瀏覽器中執行 wasm 測試。您甚至可以將這些測試整合到您的 CI 中。

在此處深入了解 wasm 測試。