WebAssemblyに入門してみた
WebAssembly
ブラウザでPostgreSQLが動く、ターミナルも動く
なんて、素晴らしい
ということで、入門
ついでに、興味のあったRustも使ってみる
Fedoraで検証してます。
動かしてみるが優先なので構成とか解説などはないです。。
環境準備
Rustのインストール
ここは手抜きでasdfを使う
asdfのインストールはGetting Startedの通りなので、省略。。。
Rustをインストールするためには、asdfにpluginをインストール
asdf plugin-list-all | grep -i rust
→ plugin-list-allで全pluginの一覧が出力される
→ めちゃくちゃ多いので、grep する
asdf plugin-add rust
asdf plugin-list
→ rustとでればpluginの追加完了
Rustをインストール
asdf install rust <バージョン>
→ バージョンはタブを押せば補完で表示されるので好きなものを
→ (例) asdf install rust 1.83.0
asdf global rust <バージョン>
→ デフォルトで使うバージョンを設定しておく
Rustでwasmを作る準備
Rust and WebAssemblyのSetupに従って準備。必要なものは以下
- rustup, rustc, cargo(Rustインストール時に含まれている)
- wasm-pack
- cargo-generate
- npm
wasm-pack, cargo-generateを入れた後は以下コマンドでshim配下を更新しとかないとPATHが通らず面倒なので、忘れずに。こちらの記事に感謝
asdf reshim rust
wasm-pack
4つばかりインストール手段があるが、cargoを選択
gccを事前にインストールしてから以下コマンド実行
cargo install wasm-pack
cargo-generate
これもcargoを選択
openssl-develを事前にインストールしておく。
cargo install cargo-generate
npm
これは、asdfに頼る
asdf plugin-add nodejs
asdf install nodejs <バージョン>
which npm
→ ~/.asdf/shims/npm
npm install npm@latest -g
→ npmをlatestにバージョンアップ
wasm32-unknown-unknown
このままチュートリアル通りwasm-pack buildしてもwasm32-unknown-unknownがないと怒られるのでいれるここに従って、以下コマンドを実行するとsysrootにwasm32-unknown-unknownを入れてくれる
rustup target add wasm32-unknown-unknown
sysrootは rustc --print sysroot
コマンドで確認可能
チュートリアル(Hello World)
上記に従って、まずはHello Worldをやってみる。
が、そのままだとエラー吐くので、修正。詳細はこちらに書きました。
チュートリアルコードを変えてみる
その1
とりあえず、練習で固定文字列を表示するだけじゃなくて、1から10までの和を表示してみる。
src/lib.rsファイルのgreet関数を以下のように変えてみて、wasm-pack build
を実行
pub fn greet() {
let mut sum = 0;
for n in 1..=10 {
sum += n;
}
alert(sum.to_string());
}
エラーになることが分かってて、やってみた。
help: consider borrowing here あたり、非常に便利。
これが言いたくて、あえてエラーケース書きました。
Compiling wasm-game-of-life v0.1.0 (/tmp/wasm-game-of-life)
error[E0308]: mismatched types
--> src/lib.rs:16:11
|
16 | alert(sum.to_string());
| ----- ^^^^^^^^^^^^^^^ expected `&str`, found `String`
| |
| arguments to this function are incorrect
|
note: function defined here
--> src/lib.rs:7:8
|
7 | fn alert(s: &str);
| ^^^^^ -------
help: consider borrowing here
|
16 | alert(&sum.to_string());
| +
ということで、正しいコードは以下
pub fn greet() {
let mut sum = 0;
for n in 1..=10 {
sum += n;
}
alert(&sum.to_string());
}
greet関数に引数をつけて呼べばテキストボックスの内容とか渡せるんだろうなぁと。
簡単な解説
Rust and WebAssembly 4.2 にも書いてるけど。
ディレクトリ構成
覚えておくのは以下でいいと思う。
wasm-game-of-life
+-- src <--- Rustのコードを格納しておく
+-- pkg <--- wasm-pack buildコマンドを実行すると作成される。wasmファイル(コンパイルされたファイル)。それを呼び出すjs,tsファイルなどが格納される
+-- www <--- フロントエンド周り
実際にRustのコードが呼び出されるまでの流れ
Rustで書いたgreet関数が呼び出されるまでの流れは以下の通り。
index.html
↓
bootstrap.js
↓
index.js
↓
greet関数
呼び出し周りはwasm-bindgenにお任せぐらいでいいかも。
何か必要なことがあれば、それはその時にお勉強で。。。
コード回り
用意されているサンプルコード
mod utils;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet() {
alert("Hello, wasm-game-of-life!");
}
おまじない
#[wasm_bindgen]
このアトリビュートがwasm-bindgenを使うところを示している。
JavaScriptの関数をRust側で使えるようにする
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
ここは、Rustから呼びたい、JavaScriptの関数を記述。C言語のexternと同じくと考えてもらえば。
上記例は、JavaScriptのalert関数を使うための定義。
externの後ろの"C"はABIを指定。この場合はC固定でいいっぽい。ちなみに指定可能なABIはこちら。
CはデフォルトなのでABIの指定は不要でもいいかも。
引数の型を何にすればいいんだろ。どこかに一覧があるのか、一定のルールがあるのか。
Rustの関数をJavaScript側で使えるようにする
#[wasm_bindgen]
pub fn greet() {
alert("Hello, wasm-game-of-life!");
}
Rust側で通常通り、関数を定義し、その上のおまじないの #[wasm_bindgen]
を書いてあげるとJavaScript側で利用可能になる模様。
ここで呼んでるalert関数は、JavaScriptの関数。
総じて
細かいところはwasm-bindgenにお任せできるので、大体の流れが理解できればよさげ。
Discussion