TL;DR

  • cargo-component を使うと、通常の Rust のプロジェクトと同じ感覚で、CLI として実行できる WebAssembly Component を作れる。
  • wasmtime コマンドで実行できる Component は wasi:cli/command World を実装しており、wasmtime-wasi を使えば Rust のプログラムからも実行できる。

内容

プロジェクトの作成とビルド

まず cargo-component をインストールする。

cargo install cargo-component

cargo component new でプロジェクトを作成する。ディレクトリ構成は通常のバイナリクレートと同じで、src/main.rs には Hello, world! を出力するコードが生成される。

cargo component new hello-wasi-cli

ビルドには cargo component build を使う。

$ cargo component build -r --target=wasm32-wasip1
...
    Creating component target/wasm32-wasip1/release/hello-wasi-cli.wasm

実行

実行するには WebAssembly のランタイムが必要である。ここでは Wasmtime を使う。

$ wasmtime target/wasm32-wasip1/release/hello-wasi-cli.wasm
Hello, world!

ライブラリクレートの利用

通常のプロジェクトと同じく、WebAssembly Component のプロジェクトでもサードパーティ製のクレートを使える。

cargo add ferris-says

cargo component add はクレートではなく WebAssembly Component を依存関係に追加するためのコマンドなので注意する。

ferris_says::say を呼ぶように src/main.rs を書き換えてビルドし、wasmtime で実行すると、カニのアスキーアートが Hello, world! を話す。

wasi:cli/command World の実装

wasmtime コマンドで実行できる WebAssembly Component は、wasi:cli/command World を実装している。これは wasm-tools で確かめられる。

$ wasm-tools component wit target/wasm32-wasip1/release/hello-wasi-cli.wasm
package root:component;
 
world root {
  import wasi:cli/[email protected];
  import wasi:cli/[email protected];
  ...
  export wasi:cli/[email protected];
}

結果から、この Component は wasi:cli/run インタフェースを export していることが分かる。つまり、ライブラリクレートと同じように、Rust のプログラムから実行できる。

wasmtime-wasi を使うと、wasi:cli/run を実装した Component を Rust から簡単に呼び出せる。詳しくは第5章で扱う。新しいプロジェクトに clap、anyhow、wasmtime、wasmtime-wasi を追加し、src/main.rs を次のように書く。

async fn start(cli: Cli) -> anyhow::Result<()> {
    let mut config = wasmtime::Config::new();
    config.async_support(true);
    let engine = Engine::new(&config)?;
 
    let mut linker = Linker::new(&engine);
    let mut store = Store::new(&engine, Ctx::new());
 
    wasmtime_wasi::add_to_linker_async(&mut linker)?;
 
    let component = Component::from_file(&engine, cli.wasm_file)?;
 
    let command =
        wasmtime_wasi::bindings::Command::instantiate_async(&mut store, &component, &linker)
            .await?;
 
    let guest = command.wasi_cli_run();
    let _ = guest.call_run(&mut store).await?;
 
    Ok(())
}

Ctx は WasiCtx と ResourceTable を持つ構造体で、WasiView トレイトを実装している。引数に先ほど生成した Wasm ファイルのパスを渡して実行すると、wasmtime で実行したときと同じ出力が得られる。

cargo run -- ../hello-wasi-cli/target/wasm32-wasip1/release/hello-wasi-cli.wasm

キーワード