1. はじめに
Rust から WebAssembly を出力してみます。
WebAssembly は、ブラウザ上で動作するバイナリフォーマットで、C/C++ や Rust などの言語からコンパイルされます。これにより、Web アプリケーションのパフォーマンスを向上させることができます。
割と昔からある技術のようですが、WebAssembly を使ったことがなかったのと、Rust も最近始めたので試してみることにしました。
2. Rust にパッケージ追加
Rust に WebAssembly を出力するためのパッケージを追加します。
rustup target add wasm32-unknown-unknown
wasm32-unknown-unknown は、WebAssembly のいくつかあるターゲットアーキテクチャの一つのようです。
ターゲットプラットフォームが unknown ということで、特定の OS や CPU アーキテクチャに依存しないことを意味しています。
一番汎用的そうですが、替わりに標準的なライブラリは利用できないようです。
3. プロジェクト作成とビルド
JavaScript から呼び出すため --lib で作成
cargo new --lib rust_wasm
cd rust_wasm
Cargo.toml に以下を追加
[lib]
crate-type = ["cdylib"]
src/lib.rs に以下を追加
#[unsafe(no_mangle)]
pub fn main() -> i32 {
println!("Hello, world!");
return 0;
}
GitHub Copilot が書いてくれました。#[no_mangle]
は、Rust のコンパイラに対して、関数名を変更しないように指示する属性です。
これがないと、Rust のコンパイラは関数名を変更してしまい、JavaScript から呼び出せなくなります。
本来は #[no_mangle] だけで良いらしいのですが、unsafe
をつけないとコンパイルエラーになりました。unsafe
は、Rust の安全性を無視することを示すキーワードで、通常は避けるべきですが、ここでは WebAssembly に出力するために必要なようです。
ビルド
cargo build --target wasm32-unknown-unknown
これで、target/wasm32-unknown-unknown/debug/rust_wasm.wasm が生成されます。
index.html を作成
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WASM Example</title>
</head>
<body>
<h1>WASM Example</h1>
<script>
async function loadWasm() {
try {
const response = await fetch('rust_wasm.wasm');
const bytes = await response.arrayBuffer();
const { instance, module } = await WebAssembly.instantiate(bytes);
// エクスポートされた関数名を一覧表示
console.log("Exported functions:", Object.keys(instance.exports));
console.log('WASM Loaded:', instance.exports.main());
// ...必要に応じてWASMインスタンスを使用するコードを追加...
} catch (error) {
console.error('Error loading WASM:', error);
}
}
loadWasm();
</script>
</body>
</html>
動いている Web サーバーに、rust_wasm.wasm を配置して、index.html をブラウザで開くと、コンソールにエクスポートされた関数名と WASM Loaded: 0 が出力されます。println!
は、WebAssembly では動作しないので、コンソールに表示されません。
一先ずこれで動作は確認できました。
4. Docker で動かす
出来上がった WASM を毎回 Web サーバーに配置するのは面倒なので、Docker で動かしてみます。
4.1 Dockerfile の準備
FROM node:alpine
RUN npm install -g http-server
WORKDIR /usr/src/app
EXPOSE 8080
CMD ["http-server", "-p", "8080"]
4.2 フォルダ構成の例
index.html と Dockerfile を rust_wasm.wasm が出力される target/wasm32-unknown-unknown/debug に配置します。
rust_wsam/
└── target
└── wasm32-unknown-unknown
└── debug
├── Dockerfile
├── index.html
└── rust_wasm.wasm
4.3 イメージをビルド
ターミナル(PowerShell や WSL など)を開いて、プロジェクトフォルダに移動して以下のコマンドを実行します。
docker build -t my-http-server .
my-http-server は好きな名前で OK。
. は現在のディレクトリをコンテキストとして指定。
4.4 ボリューム付きコンテナを起動
docker run -d -p 8080:8080 -v ${PWD}:/usr/src/app my-http-server
4.5 ブラウザで確認
ブラウザで http://localhost:8080 を開くと、コンソールにエクスポートされた関数名と WASM Loaded: 0 が出力されます。
これでビルドし直してもブラウザをリロードするだけで、WASM を確認できるようになりました。
5. まとめ
Rust から WebAssembly を出力し、Docker のコンテナから確認するところまでやってみました。
WebAssembly を使う準備はできたので、次は実際に WebAssembly を使って何か作ってみたいと思います。
今回の記事も、Github Copilot が7割くらい書いてくれました。
A. 参考サイト
WebAssembly - Rustプログラミング言語
Rust での非 Web 向け Wasm モジュール作成
Rust で WebAssembly を出力する
5 分で WebAssembly + Docker = Hello World
コメント