🙆

RustからWebAssemblyをコンパイルしてDenoで動かす

2023/09/10に公開

某ゲーム開発入門の参考書を買ったのでまずは環境をセットアップしました。基本的には公式チュートリアルの通りですが、Denoを使用するにあたり一部修正した部分をまとめました。

ビルドにはwasm-packを使用しています。

https://developer.mozilla.org/ja/docs/WebAssembly/Rust_to_Wasm

インストール

それぞれ公式サイトにある方法を記載します。ディストリビューションによってはリポジトリに存在する場合があるので、そちらからインストールしてもOkです。

Rust

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ rustup toolchain install stable

wasm-pack

$ cargo install wasm-pack

Deno

$ curl -fsSL https://deno.land/x/install/install.sh | sh

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

Rustのライブラリプロジェクトを作成し、コードと依存関係を編集します。中身の説明については公式のチュートリアルを参照ください。とりあえずは#[wasm_bindgen]がついている関数が使えるようになるという雑な理解で大丈夫です。

$ cargo new --lib hello-wasm
     Created library `hello-wasm` project
$ cd hello-wasm
$ tree
    ├── Cargo.toml
    └── src
       └── lib.rs
src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}

#[wasm_bindgen]
pub fn square(x: u32) -> u32 {
    x * x
}
Cargo.toml
[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
description = "A sample project with wasm-pack"
license = "MIT/Apache-2.0"
repository = "https://github.com/yourgithubusername/hello-wasm"
edition = "2018"

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

[dependencies]
wasm-bindgen = "0.2"

その後、wasm-packコマンドでビルドします。

この時、--targetオプションでビルドターゲットを設定します。今回はDenoで実行するためdenoに設定してください。webbundlerの場合でも、*.rsCargo.tomlファイルの中身は共通で使用できます。

オプション 内容
bundler デフォルト。Webpackハンドラーとしてロードする
web Webブラウザで直接ロードする
nodejs Nodejsのモジュールとしてロードする
deno Denoモジュールとしてロードする
no-module webに近いがESモジュールを使用しない

https://rustwasm.github.io/docs/wasm-bindgen/reference/deployment.html

--out-dir, --out-nameオプションはなくても動きますが、覚えておくとDenoプロジェクト内で使用するのに便利です。詳しくはwasm-pack build --helpを確認ください。

$ wasm-pack build --target deno --out-dir pkg --out-name wasm
[INFO]: 🎯  Checking for the Wasm target...
[INFO]: 🌀  Compiling to Wasm...
   Compiling proc-macro2 v1.0.66
   ...
   Compiling hello-wasm v0.1.0 (/home/user/Deno/hello-wasm)
    Finished release [optimized] target(s) in 3.22s
[INFO]: ⬇️  Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: ✨   Done in 3.48s
[INFO]: 📦   Your wasm pkg is ready to publish at /home/user/Deno/hello-wasm/pkg.

$ tree
    ├── Cargo.lock
    ├── Cargo.toml
    ├── pkg
    │  ├── wasm.d.ts
    │  ├── wasm.js
    │  ├── wasm_bg.wasm
    │  └── wasm_bg.wasm.d.ts
    ├── src
    │  └── lib.rs
    └── target
       ├── CACHEDIR.TAG
       ├── debug
       ├── release
       └── wasm32-unknown-unknown

Denoで実行

出力されたjsファイルを呼び出して実行します。

main.ts
import { greet, square } from "./pkg/wasm.js";

greet("John");

const x = 4;
console.log(`square ${x} is ` + square(x));
$ deno run -A hello-wasm.ts
Hello, John! [Enter]
square 4 is 16

参考

https://rustwasm.github.io/

https://crates.io/crates/wasm-pack

https://crates.io/crates/wasm-bindgen

Discussion