【Rust】WASMにコンパイルしてDenoから呼び出す方法

2022/06/18に公開

Rustで書いたモジュールを Webassembly にコンパイルして Deno から呼び出して実行します。
Denoはasyncなしで、awaitが使用可能なので、Wasmの読み込みもすっきり記述することができそうです。

ポイント

  • Rustでモジュールを作成する
  • Webassembly にコンパイルする
  • Deno から呼び出す

関連

https://zenn.dev/shinkano/articles/280518001ad0d6

準備

Denoのインストール

https://deno.land/

Rustのインストール

https://www.rust-lang.org/

WASM関連の依存関係インストール

コンパイラ

rustup target add wasm32-unknown-unknown

GC

cargo install wasm-gc

Rustモジュールの作成

クレートの作成

以下のコマンドを実行し、wasm_deno_example クレートを作成します。

cargo new --lib wasm_deno
cd wasm_deno

プロジェクトファイル群が生成されます。
Cargo.toml ファイルに、以下を追記します。

[lib]
crate-type =["cdylib"]

cdylibは、RustプロジェクトをWASMのような他の言語でも使えるようにするために必要です。

Rustプログラムを書く

今回は src/lib.rs に以下のシンプルな関数を書きました。

src/lib.rs
#[no_mangle]
pub extern "C" fn square(x: u32) -> u32 {
    x * x
}
  • #[no_mangle] をつけるとマングリング[1]されなくなります。
  • externは、他の言語(ここではDeno)から呼び出すために必要な宣言です。

WASMにコンパイルする

以下のコマンドでコンパイルします。

cargo build --target wasm32-unknown-unknown

出力されたファイルをGCツールにかけることで、ファイルサイズを小さくすることができます。

wasm-gc target/wasm32-unknown-unknown/debug/wasm_deno.wasm

Denoから呼び出す

main.ts ファイルを作成します。
以下のようにWASMを読み込んで実行するコードを記述します。

main.ts
// 1.Wasmをロード
const wasmCode = await Deno.readFile("./target/wasm32-unknown-unknown/debug/wasm_deno.wasm");
// 2.ロードしたファイルからWasmモジュールを作成
const wasmModule = new WebAssembly.Module(wasmCode);
// 3.関数を使用できるようモジュールのインスタンスを作成
const wasmInstance = new WebAssembly.Instance(wasmModule);

const { square } = wasmInstance.exports;

console.log(square(1)); // 1
console.log(square(2)); // 4
console.log(square(3)); // 9
console.log(square(4)); // 16

実行します。

deno run --allow-read main.ts
# Check file:///var/www/html/main.ts
# 1
# 4
# 9
# 16

WASMのモジュールが実行され、結果が出力されました。


参考

https://dev.to/lampewebdev/writing-webassembly-in-rust-and-runing-it-in-deno-144j
https://doc.rust-jp.rs/book/second-edition/ch01-01-installation.html
https://blog.mzumi.com/post/2016/10/18/hello-ffi/

脚注
  1. マングリング: ざっくり言うと、関数名や変数名がエンコードされたりなどして変わること。 ↩︎

Discussion