Edited at

爆速でjsでWebAssemblyを動かす


概要

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からアクセスできるようにするための記述である。


src/lib.rs

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.wasmjs_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.jsonwebpack.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