🚲

Rust を WebAssembly にコンパイルして Vite で使ってみる

2023/12/04に公開

はじめに

2023 年 11 月から弊社で TRPL の輪読会を週一で開催しています。そろそろ何かを動かしたい衝動に駆られたので、自分のホームページで Rust からコンパイルした WebAssembly を使ってみることにしました。

まだ「Rust 何も分からん」な筆者ですが、まるで実用性のない塊を作ってみます。

https://github.com/kazuhe/kazuhe.github.io/tree/050136bc43606d585068cae905313763c4f50bbd

また、この記事は既に Vite のプロジェクトと Rust を利用できる前提で書いています。

Rust から WebAssembly にコンパイル

実は MDN にすごく分かりやすいチュートリアルがあります。

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

なので MDN を見てください。一応、長い文章を見たくない人のために簡単にやったことを書きます。

WebAssembly にコンパイルしたり、npm ライブラリとして公開する機能を提供してくれる wasm-pack をインストールします。

cargo install wasm-pack

次に Rust のコードを置くディレクトリを作成します。

mkdir packages

移動して cargo new をします。

cargo new --lib wasm-kazuhe-github-io

Cargo.tomlsrc/lib.rs が作成されるので、チュートリアルに従って JavaScript から window.alert を受け取り、greet 関数の中で実行するだけの極めて単純なファイルを作ります。

/packages/src/lib.rs
use wasm_bindgen::prelude::*;

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

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

wasm-bindgen の依存の追加とその他設定を Cargo.toml に追記します。

Cargo.toml
[package]
name = "wasm-kazuhe-github-io"
version = "0.1.0"
authors = ["Ohara Kazuhiro"]
description = "WebAssembly package for my website."
repository = "https://github.com/kazuhe/kazuhe.github.io"
edition = "2021"

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

[dependencies]
wasm-bindgen = "0.2.84"

npm ライブラリとして公開するために wasm-pack でコンパイルを実行します。

wasm-pack build packages --target bundler

これで packages/pkgpackage.json.wasm ファイル、型定義ファイル (.d.ts) が生成され、WebAssembly の npm ライブラリができました。

WebAssembly の npm ライブラリを公開

npm のアカウントが必要なので、なければ作成しログインします。その後 publish コマンドを実行するだけで簡単に公開ができます。

(私以外使わないのに、わざわざ npm ライブラリとして公開するのも大袈裟ですが練習として公開してみました)

wasm-pack login
wasm-pack publish

https://www.npmjs.com/package/wasm-kazuhe-github-io

これで npm ライブラリを使う準備ができました。

Vite プロジェクトからの利用

普通の npm ライブラリをインストールするのと同じようにインストールします。

pnpm i -E wasm-kazuhe-github-io

Rust で定義した greet 関数をインポートして実行します。

/src/index.ts
import { greet } from "wasm-kazuhe-github-io";

greet("wasm");

生成された npm ライブラリには型定義ファイルも含まれているので、もちろん型の補完も効きます。

エディタ

index.html に TypeScript ファイルを読み込みます。

index.html
<body>
  <!-- ~~ -->
  <script type="module" src="/src/index.ts"></script>
</body>

現時点では、このままの状態だと WebAssembly が ESM と統合されていないため動作しません。ただ、ESM 統合の提案があり、将来的には動作することが期待されているようです。

https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration

なので、vite-plugin-wasm を追加します。

まだ top-level await がサポートされていないブラウザもあるようなので、ついでに vite-plugin-top-level-await も追加します。

https://v8.dev/features/top-level-await#support

v8.dev

vite.config.ts を以下のように書きます。

vite.config.ts
import { defineConfig } from "vite";
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";

export default defineConfig({
  plugins: [wasm(), topLevelAwait()],
});

これを GitHub Pages にデプロイしてみると..

kazuhe.github.io

.wasm ファイルが読み込まれて window.alert が実行されていることを確認できました。

さいごに

今の状態では、まるで実用性のない機能ですが 簡単に WebAssembly を試すことができました。

Discussion