爆速でjsでWebAssemblyを動かす

# 概要
jsからRustにより出力されたwasmを呼び出すための方法を記載する。
Rustを動かすための環境構築の方法は[“プログラミング言語Rust: 2nd Edition”の日本語版PDFを作成した](https://qiita.com/yyu/items/18df35988f3ea3b12af0)を参考にしていただきたい。

(2019/09/26 追記)しばらく前にこれやってみて久しぶりに動かしたけど動かない、という人は、以下を実行すればなおる。

```
rustup update
rustup update nightly
cargo install -f wasm-bindgen-cli
```

早速本題に入っていく。

# クロスコンパイルのターゲットの追加
これを使ってwebassemblyにしてください、という指定を行う。ここでは`wasm32-unknown-unknown`を追加する。[wasm-bindgenのgit](https://github.com/rustwasm/wasm-bindgen)ではnightlyの指定をしていないが、私の環境ではうまく動かなかったので`+nightly`を指定してターゲットを追加する。
参考：[nightlyのインストール方法](https://doc.rust-lang.org/1.15.1/book/nightly-rust.html)

```
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を呼び出すために必要、という理解でよい。

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

```rust: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.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
<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メソッドを利用できるようになる。

```js
const js = import("./js_hello_world");

js.then(js => {
  js.greet("hello!!");
});

```
# サーバの準備
ここまでできたらサーバは何でもよいのだが、私はwebpackのビルトインサーバを利用して動作を確認するため、利用した`package.json`と`webpack.js.config`を以下に記載する。

## package.json

```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

```js
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](https://github.com/mk-tool/wasm_sample)に公開しています。

# 参考
[Rust単体でWebAssemblyをコンパイルする（Emscripten無し）](https://sbfl.net/blog/2017/11/27/rust-webassembly-standalone/)
[JavaScript to Rust and Back Again: A wasm-bindgen Tale – Mozilla Hacks – the Web developer blog](https://hacks.mozilla.org/2018/04/javascript-to-rust-and-back-again-a-wasm-bindgen-tale/)