概要
jsからRustにより出力されたwasmを呼び出すための方法を記載する。
Rustを動かすための環境構築の方法は“プログラミング言語Rust: 2nd Edition”の日本語版PDFを作成したを参考にしていただきたい。
(2019/09/26 追記)しばらく前にこれやってみて久しぶりに動かしたけど動かない、という人は、以下を実行すればなおる。
rustup update
rustup update nightly
cargo install -f wasm-bindgen-cli
早速本題に入っていく。
クロスコンパイルのターゲットの追加
これを使ってwebassemblyにしてください、という指定を行う。ここではwasm32-unknown-unknown
を追加する。wasm-bindgenのgitではnightlyの指定をしていないが、私の環境ではうまく動かなかったので+nightly
を指定してターゲットを追加する。
参考:nightlyのインストール方法
rustup target add wasm32-unknown-unknown
# もしくは
rustup target add wasm32-unknown-unknown --toolchain nightly
Rustのプロジェクトを作成
プロジェクトを作成する。今回はwasmをモジュールとして読み込ませたいので、--lib
をつける。
cargo new js-hello-world --lib
cd js-hello-world
Cargo.toml
以下のように記載する。cdylib
はRustコードをコンパイルするためのツールで、wasm-bindgenはjsでrustのAPIを呼び出すために必要、という理解でよい。
[package]
name = "js-hello-world"
version = "0.1.0"
authors = ["gamushiro <>"]
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
モジュールを書く
細かな文法等の説明はここではしないが、引数に応じてalert()を行うgreetという関数をつくる。
#[wasm_bindgen]
をしている箇所ではwasm_bindgen
を利用した際に、javascriptからアクセスできるようにするための記述である。
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
その後ビルドを行う(ここでも私の環境ではnightlyを指定した。そうしないとビルドが通らなかった)。
cargo build --target wasm32-unknown-unknown
# もしくは
cargo +nightly build --target wasm32-unknown-unknown
wasm-bindgen-cliのインストール
wasm-bindgenコマンドを利用するためにwasm-bindgen-cli
をインストールする。
wasm-bindgenコマンドによりwasmファイルとwasmを呼び出すjsのインターフェースの役割をするjsファイルが出力される。
cargo install wasm-bindgen-cli
wasm-bindgenの実行
もともとビルドされたwasmファイルを1番目のオプションに、出力先を2番目のオプションを指定する。
js_hello_world_bg.wasm
とjs_hello_world.js
が出力されるが、出力されたファイルを使ってjavascriptからアクセスをする(後程jsを書く際に再度触れる)。ちなみに、プロジェクト名がハイフン区切りの場合でも、出力されているwasmファイルはアンダースコア区切りになっていることに注意。
wasm-bindgen target/wasm32-unknown-unknown/debug/js_hello_world.wasm --out-dir .
大体これでwasm側の準備ができたので、これからhtmlをサーブするための準備をする。
html
特に気にせず。
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
</head>
<body>
<script src='./index.js'></script>
hello from rust !!
</body>
</html>
js
ここでwasm-bindgenにより出力されたjsファイルを読み込む。そうすることでwasmファイルへのアクセスが可能になり、greetメソッドを利用できるようになる。
const js = import("./js_hello_world");
js.then(js => {
js.greet("hello!!");
});
サーバの準備
ここまでできたらサーバは何でもよいのだが、私はwebpackのビルトインサーバを利用して動作を確認するため、利用したpackage.json
とwebpack.js.config
を以下に記載する。
package.json
{
"scripts": {
"serve": "webpack-dev-server"
},
"devDependencies": {
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.11.1",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.0"
}
}
webpack.js.config
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
},
plugins: [
new HtmlWebpackPlugin()
],
mode: 'development'
};
これで
yarn run serve
を行いlocalhost:8080
にアクセスすれば画面読み込み時にアラートが出てくる。
ソースコードはGitHubに公開しています。
参考
Rust単体でWebAssemblyをコンパイルする(Emscripten無し)
JavaScript to Rust and Back Again: A wasm-bindgen Tale – Mozilla Hacks – the Web developer blog