Contents
概要と必要なツール
| ツール | 役割 | 推奨バージョン |
|---|---|---|
| Rust (stable) | 言語本体・コンパイラ (rustc) と Cargo パッケージマネージャー | 最新の安定版 |
| wasm-bindgen | Rust の型や関数を JavaScript にエクスポートするためのバインディングコード生成 | cargo add wasm-bindgen で自動取得 |
| wasm-pack | ビルド、パッケージ化、npm 公開までを一括管理 | 最新の安定版(インストール時に自動取得) |
ポイント
-wasm-bindgenが生成する JS ラッパーは ES モジュール形式で提供されるため、Node.js なしでもブラウザ単体で動作します。
-wasm-packはビルド・型定義生成・npm パブリッシュを自動化し、CI/CD と相性が良い点が特徴です。
参考情報:
- MDN の公式チュートリアル「Rust to Wasm ガイド」(MDN contributors, 2024‑02‑15)。
- Zenn 記事「WebAssembly 入門」by summer_fb (2024‑07‑20)【リンク】。
開発環境のセットアップ
1. Rust のインストールとツールチェーン管理
|
1 2 3 4 5 6 7 8 9 |
# rustup が未導入の場合は公式インストーラを実行 curl https://sh.rustup.rs -sSf | sh # インストール後、最新安定版コンパイラを取得 rustup update stable # WebAssembly 用ターゲットを追加 rustup target add wasm32-unknown-unknown |
2. wasm-pack と補助ツールの導入
|
1 2 3 4 5 6 |
# Cargo 経由で最新版の wasm-pack をインストール cargo install wasm-pack --locked # プロジェクトテンプレート生成に便利な cargo-generate (任意) cargo install cargo-generate |
ポイント
rustupが提供するツールチェーンはシステム全体で一元管理でき、ターゲット追加やアップデートがコマンド一つです。wasm-packはインストール時に自動的に最新版を取得しますので、バージョン番号を書き込む必要はありません。
3. プロジェクトの雛形作成
方法 A: cargo generate
|
1 2 3 |
cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name wasm_demo cd wasm_demo |
方法 B: wasm-pack init(シンプルな構成向け)
|
1 2 3 |
mkdir wasm_demo && cd wasm_demo wasm-pack new . |
どちらの方法でも、src/lib.rs が自動生成され、最小限の Cargo 設定が整います。
プロジェクト作成とコード実装
1. #[wasm_bindgen] アトリビュートで関数をエクスポート
以下は文字列を逆順に返す純粋関数と、ページロード時に自動実行される初期化ロジックの例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// src/lib.rs use wasm_bindgen::prelude::*; use web_sys::{window, Document}; /// 受け取った文字列を逆順にして返す関数 #[wasm_bindgen] pub fn reverse(input: &str) -> String { input.chars().rev().collect() } /// モジュールが読み込まれたときに呼び出されるエントリポイント #[wasm_bindgen(start)] pub fn run() -> Result<(), JsValue> { // コンソールへデバッグメッセージを出力 web_sys::console::log_1(&"Wasm モジュールが初期化されました".into()); // 簡易的にボタンのクリックハンドラを設定 let document: Document = window().unwrap().document().unwrap(); let btn = document.get_element_by_id("btn") .expect("button element not found"); // Closure を生成し、JS 側へ渡す let closure = Closure::wrap(Box::new(move || { web_sys::console::log_1(&"ボタンがクリックされました".into()); }) as Box<dyn FnMut()>); btn.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())?; // Closure を忘れさせてメモリリークを防止 closure.forget(); Ok(()) } |
重要ポイント
| 項目 | 説明 |
|---|---|
#[wasm_bindgen] | 関数や構造体を JavaScript から呼び出せるようにする属性。 |
#[wasm_bindgen(start)] | モジュール読み込み時に自動実行され、初期化処理を書ける。 |
web-sys クレート | ブラウザ API(DOM、Console など)への安全なアクセスを提供。 |
Closure::wrap | Rust のクロージャを JavaScript 側の関数オブジェクトに変換するユーティリティ。 |
ビルド・ブラウザ組み込み手順
1. wasm-pack によるビルド
|
1 2 |
wasm-pack build --target web |
--target webを指定すると Node.js 用バンドラは不要 な形で出力され、生成物は以下のようにpkg/ディレクトリに配置されます。
| ファイル | 内容 |
|---|---|
wasm_demo_bg.wasm | 実際の WebAssembly バイナリ |
wasm_demo.js | ES モジュール形式の JavaScript ラッパー |
wasm_demo_bg.d.ts | TypeScript 用型定義ファイル |
wasm_demo_bg.wasm.map | デバッグ用 source map |
ポイント
生成された.jsはimport文で直接読み込めるため、Webpack や Rollup といったバンドラは必須ではありません。
2. HTML 側からのロード例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Rust + Wasm デモ</title> </head> <body> <input id="text" placeholder="文字列を入力してください"> <button id="btn">逆順にする</button> <p id="result"></p> <!-- ES モジュールとして wasm-pack が生成したファイルを読み込む --> <script type="module"> import init, { reverse } from './pkg/wasm_demo.js'; async function main() { await init(); // .wasm の取得とインスタンス化 document.getElementById('btn').addEventListener('click', () => { const src = document.getElementById('text').value; const out = reverse(src); document.getElementById('result').textContent = out; }); } main(); </script> </body> </html> |
補足
init()は自動的にWebAssembly.instantiateStreamingを呼び出し、ネットワーク経由で.wasmバイナリを取得します。- ブラウザが モジュールスクリプト に対応していれば、追加のビルドステップは不要です。
デバッグ・テスト手法
1. Source Map を活用したブラウザデバッグ
wasm-pack が生成する *.wasm.map ファイルを有効化すると、Chrome/Firefox の DevTools で Rust ソースレベル のブレークポイントが設定可能です。
手順:
- DevTools → Settings → Enable WebAssembly debugging をオンにする。
Sourcesタブでwasm_demo_bg.wasm.mapが自動的にロードされ、src/lib.rsが表示されることを確認。- 任意の行(例:
reverse関数内部)にブレークポイントを置き、ページ上で操作すると実行が停止します。
ポイント
ソースマップはデバッグ時だけ有効化し、本番ビルドでは除外してサイズを削減できます。
2. wasm-objdump によるバイナリ確認
|
1 2 3 4 5 |
# Ubuntu 系の場合 (wabt パッケージに同梱) sudo apt-get install wabt # 包含 wasm-objdump wasm-objdump -d pkg/wasm_demo_bg.wasm | less |
出力例(抜粋):
|
1 2 3 4 |
0000100: 00 61 73 6d 01 00 00 00 ... ; Wasm ヘッダー 001020: 41 05 i32.const 5 001022: 10 00 call #0 ; reverse 関数呼び出し |
この情報は 最適化の過程で意図しないコードが生成されていないか を目視で確認したいときに有用です。
3. JavaScript コンソールからの即時呼び出し
|
1 2 3 4 |
import init, * as wasm from './pkg/wasm_demo.js'; await init(); console.log(wasm.reverse('hello')); // => "olleh" |
テストスクリプトを npm test などに組み込めば、CI 上でも簡易的な動作確認が行えます。
静的ホスティングと CI 設定
1. GitHub Pages / Cloudflare Pages へのデプロイ
生成された pkg/ ディレクトリ(または static/ にコピーしたもの)をそのまま静的サイトとして公開できます。
- GitHub Pages
gh-pagesブランチを作成し、pkg/の内容をルートに配置。-
リポジトリ設定 → Pages → 「Source: gh‑pages」へ切り替える。
-
Cloudflare Pages
- プロジェクトのビルドコマンドは
wasm-pack build --target web、出力ディレクトリはpkgを指定。 - デプロイ完了後に自動的に CDN が有効化され、低遅延で Wasm が配信されます。
参考: Qiita 記事「Node なしフロントエンド」by osanshouo (2023‑11‑05)【リンク】。
2. GitHub Actions による自動ビルド & デプロイ
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# .github/workflows/deploy.yml name: Deploy Wasm to GitHub Pages on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - name: Install Rust toolchain run: | rustup update stable rustup target add wasm32-unknown-unknown - name: Install wasm-pack run: cargo install wasm-pack --locked - name: Build Wasm package run: wasm-pack build --target web - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./pkg |
- ステップ解説
rustupとwasm-packのインストールは毎回最新安定版が取得されます。wasm-pack build --target webにより、Node.js 不要の静的アセットだけがpkg/に出力されます。- 最後に
peaceiris/actions-gh-pagesアクションでgh-pagesブランチへ自動プッシュし、GitHub Pages が即座に更新されます。
ポイント
CI の構成はシンプルながらも、ローカルと同一のビルドフローを保証できるため「ローカルで動く → 本番でも動かない」問題がほぼ解消します。
まとめと次のステップ
| 項目 | 内容 |
|---|---|
| エコシステム | wasm-bindgen がバインディング、wasm-pack がビルド・配布を担当。どちらも最新安定版を利用すればバージョン管理の煩雑さが減ります。 |
| 環境構築 | rustup → wasm32-unknown-unknown ターゲット追加 → cargo-generate/wasm-pack init でプロジェクト雛形作成。 |
| コード実装 | #[wasm_bindgen] アトリビュートで関数やエントリポイントを公開し、web-sys でブラウザ API に安全にアクセス。 |
| ビルドと組み込み | wasm-pack build --target web → pkg/ の ES モジュールを HTML の <script type="module"> からインポートすれば、Node.js 不要で動作。 |
| デバッグ | .wasm.map と DevTools によるソースレベルブレークポイント、wasm-objdump でバイナリ確認が可能。 |
| デプロイ | pkg/ をそのまま GitHub Pages / Cloudflare Pages に配置し、GitHub Actions で自動ビルド・公開を実装。 |
この流れに沿えば、最新の安定版 Rust と Wasm の組み合わせで、余計な依存なしに高速かつ安全なフロントエンド機能をすぐにプロトタイプ化できます。
次に挑戦したいこと
-trunkやwasm-bindgen-cliを使った SPA(シングルページアプリ)構築。
- 大規模クレート群でのコード分割と遅延ロード実装。
- CI に cargo audit と wasm-opt(Binaryen)を組み込み、セキュリティとパフォーマンス最適化を自動化。
ぜひ本稿の手順をベースに、独自の WebAssembly アプリケーション開発に挑戦してみてください。