javaScriptとWebAssembly（seed使用）を相互連携してみる。

# この記事について

発端は、Rustを勉強する為に、ルービックキューブアプリをRust＋WebAssemblyで作成し始めた事になります。
詳細は[こちらの自分記事](https://qiita.com/etnk/items/35de53198e1c9f1d6959)に書いてあります。

その後少しずつ進めて、ルービックキューブアプリとして遊べるぐらいまでは進めました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866006/dadfef11-4d68-aa45-7604-b9d942ef0bac.png)
ただ、ここまでくると当初は「そこまで踏み込まなくて良いだろう」と思っていた事にチャレンジしたくなってきます。
何かと言うと、見た目です。そもそもデザインのセンスは無いので、オサレな画面にしようなどという野望は持っていませんが、さすがにボタンなどのデザインは世の中のリソースを活用したくなってきます。
今後、基本操作だけでなく各種機能を付けたくなった時、seed（Rust用WebAssemblyのフレームワークの一つ。現在使用中）で全て実現するには技術的に可能だったとしても、情報を見つけられるか、見つけられたとしても調査に結構時間がかかるだろう、世のWebUIフレームワークと連携出来る様にしておきたいという判断もあります。

# 目標

javaScript側で、WebAssembly（seed）とやり取りを行う方法を確認します。最終目標はVue、ReactなどのリッチUIフレームワークから使用する事ですが、この記事ではjavaScript側で使用するステップまで行います。

具体的には以下の技術要素の確認を行います。
- javaScript側からWebAssemblyの関数を呼び出す
   - 関数の引数で情報を渡す
   - 関数の返り値をjavaScript側で受け取る
   - seedのレンダリングをトリガーする
- WebAssemblyからjavaScriptの関数を呼び出す
   - 関数の引数で情報を渡す
   - 関数の返り値をjavaScript側で受け取る
   - javaScript側でその値を使用して何か処理する

# 基本技術確認

seedの公式gitのexamplesに２つ良さそうなのがあります。

- [rust_from_js](https://github.com/seed-rs/seed/tree/master/examples/rust_from_js)
  実行した後に、開発者ツールで引数付きでjavaScriptコマンドを実行し、Renderボタンを押すと seed側を反映するサンプル
  Readmeには、詳細なサンプルは後述`update_from_js`を見てと書いてあります。
- [update_from_js](https://github.com/seed-rs/seed/tree/master/examples/update_from_js)
  javaScriptのタイマーでjs関数を実行し、seed側で時間表示するサンプル

## [rust_from_js](https://github.com/seed-rs/seed/tree/master/examples/rust_from_js) の技術要素確認

２つのうちの基本的だと思われる方を確認します。順を追って確認します。

### index.html（javaScript側）

```html
<body>
  <section id="app"></section>
  <script type="module">
    import { set_title } from '/pkg/package.js';
    window.rust = { set_title };

    import init from '/pkg/package.js';
    init('/pkg/package_bg.wasm');
  </script>
</body>
```

`/pkg/package.js`はseedのコンパイル生成物です。seed内の`set_title`という関数をjavaScript側の`rust`配下に割り当てるという事をやっているようです。

### set_title

lib.rs（WebAssembly側）の該当関数を調べます。

```rust
#[wasm_bindgen]
pub fn set_title(title: String) {
    TITLE.with(|title_cell| title_cell.replace(title));
}
```

wasm_bindgenの指定をする事で、javaScript側から呼べるようにしているようです。ちょっと寄り道して、`TITLE`なるオブジェクトの中身を見てみます。なんか複雑な変数ですね。

```rust
thread_local!(static TITLE: RefCell<String> = RefCell::new("I'm TITLE!".to_owned()));
```

上記２ブロックで出てきた単語を調べます。

- thread_local・・・複数スレッドから安全に参照出来る様にする為の技術
- RefCell・・・情報をラッピングする事で、不変の様に見えて内部の値を可変にする仕組み
- to_owned・・・""括り文字列（&str）をStringに変更する手法の一つ
- with・・・ちょっと良い情報が見つかりませんでしたが、クロージャを使って内部の値を操作出来る様です。
- replace・・・[RefCellの関数](https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.replace)で中身の交換を行う

という事で、記述は複雑ですが、スレッドセーフな変数（？）の宣言とその変更を行う関数の宣言ですね。

### 開発者ツール実行

Readmeには開発者ツールで以下命令実行とあります。

```javascript
rust.set_title("New title")
```

`window.rust`は/pkg/package.jsから配下に`set_title`をimportしているのでそれが呼ばれる事で、WebAssembly側内部変数を変えているという事になります。

### Render実行

開発者ツールでコンソールしても画面表示は変わりません。ボタンをクリックする事で反映します。

```rust
fn title() -> String {
    TITLE.with(|title_cell| title_cell.borrow().clone())
}
// ・・省略・・
fn view(_: &Model) -> Vec<Node<Msg>> {
    vec![
        h1![title()],
        button!["Rerender", ev(Ev::Click, |_| Msg::Rerender)],
    ]
}
```

通常は、modelに入った変数を参照しますが、javaScript側からmodelの変更が出来ない為、static変数（？）を参照する関数で値を取得しています。

## [update_from_js](https://github.com/seed-rs/seed/tree/master/examples/update_from_js) の技術要素確認

rust_from_jsでは出来ていなかったWebAssembly側のレンダリング対応や、WebAssembly側からのjavaScript関数実行も含まれます。真似すれば行けそうです。ポイントを確認していきます。

### index.html

```html
<body>
  <section id="app"></section>
  <script src="/public/index.js" type="module"></script>
</body>
```

外部ファイルを読み込むようになってますね。index.jsを見てみます。

```javascript
import init from '/pkg/package.js';
import { start } from '/pkg/package.js';

window.enableClock = () => {
  const sendTick = () => {
    tick(new Date().toLocaleTimeString());
  };
  sendTick();

  setInterval(() => {
    sendTick();
  }, 1000);
};

init('/pkg/package_bg.wasm').then(() => {
  const [js_ready, tick] = start();
  window.tick = tick;
  js_ready(true);
});
```

WebAssemblyの初期処理が終わった後、importしておいたWebAssemblyのstart関数を呼び、js_ready,tick関数を取得し、tickをグローバル関数に指定する事をしています。そして、js_ready命令を実行してWebAssembly側に準備完了を伝えているようです。
一度`enableClock`が実行されると、1秒ごとに`sendTick`が実行される形ですね。そして`sendTick`では、tickが呼ばれます。`enableClock`関数はjavaScript側では呼ばれないので、WebAssembly側で呼ばれるようです。

### WebAssembly側のenableClock

```rust
#[wasm_bindgen]
extern "C" {
    fn enableClock();
}
```

`extern "C"`というのは、C言語同様に外部からアクセスを提供する事を可能にするそうです。これにより、WebAssembly側でjavaScript側のenableClock関数を呼び出す事が可能になります。

### WebAssembly側のstart関数

`wasm_bindgen(start)`でなく`wasm_bindgen`を指定しています。javaScript側で明示的に実行し、返り値を得る為に自動実行しない様にしている様です。

```rust
#[wasm_bindgen]
// `wasm-bindgen` cannot transfer struct with public closures to JS (yet) so we have to send slice.
pub fn start() -> Box<[JsValue]> {
    let app = App::start("app", init, update, view);

    create_closures_for_js(&app)
}
```

### WebAssembly側、javaScript側で呼ばれる関数指定

javaScript側では、start関数で関数としての戻り値を受け取ってます。それの供給元です。

```rust
fn create_closures_for_js(app: &App<Msg, Model, Node<Msg>>) -> Box<[JsValue]> {
    let js_ready = wrap_in_permanent_closure(enc!((app) move |ready| {
        app.update(Msg::JsReady(ready));
    }));

    let tick = wrap_in_permanent_closure(enc!((app) move |time| {
        app.update(Msg::Tick(time));
    }));

    vec![js_ready, tick].into_boxed_slice()
}

fn wrap_in_permanent_closure<T>(f: impl FnMut(T) + 'static) -> JsValue
where
    T: wasm_bindgen::convert::FromWasmAbi + 'static,
{
    // `Closure::new` isn't in `stable` Rust (yet) - it's a custom implementation from Seed.
    // If you need more flexibility, use `Closure::wrap`.
    let closure = Closure::new(f);
    let closure_as_js_value = closure.as_ref().clone();
    // `forget` leaks `Closure` - we should use it only when
    // we want to call given `Closure` more than once.
    closure.forget();
    closure_as_js_value
}
```

２つの関数は、一度Vecに保存した後、`into_boxed_slice`にBoxに変換してます。
JsValueですが、[公式ページ](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)によるとjavaScript側で保有されるオブジェクトとの事です。

私の様な初心者にとっては難しい書式が並んでいます。詳細まで理解するのは今回は諦めます。この書式を真似れば、引数を取って、app（WebAssembly本体（？））のupdate関数を呼び出す関数をjavaScript側に返す事が出来るという事です。
update関数には、処理対象のMsgを指定います。update関数を使用する事で、WebAssembly側のレンダリングしています。

# 自分でチャレンジ

調査した情報を元に、自分でもやってみます。

## 準備

### WebAssembly（seed）

手前味噌ですが[Seed Quickstart トレース](https://qiita.com/etnk/items/35de53198e1c9f1d6959#seed-quickstart-%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B9)を真似します。

プロジェクト名は`seed-jstest`としておきます。

## javaScript側からWebAssemblyの関数を呼び出す

### index.html

WebAssembly側の関数を実行する為のボタンを用意しておきます。setWasmValueという関数を実行する様にしておきます。index.jsを呼び出すようにしておきます。

```html
<body>
    <section id="seedapp"></section>
    <button onclick="setWasmValue(123)">Increment</button>
    <script src="/public/index.js" type="module"></script>
</body>
```

### public/index.js

seedプロジェクトフォルダにpublicフォルダを作り、その配下にindex.jsを作成します。
WebAssemblyの準備及びstartから返ってきた関数を、setWasmValue関数をjavaScriptのグローバルオブジェクトにセットしておきます。

```javascript
import init from '/pkg/package.js';
import { start } from '/pkg/package.js';

init('/pkg/package_bg.wasm').then(() => {
  const [set_wasm_value] = start();
  window.setWasmValue = set_wasm_value;
});
```

### Cargo.toml

update_from_jsで使用している、encloseをdependenciesに追記します。

```toml
[dependencies]
seed = "0.8.0"
enclose = "1.1.8" # 追記
```

### lib.rs

start、create_closures_for_js、wrap_in_permanent_closure関数は丸ごとupdate_from_jsのものをコピーして、create_closures_for_js関数の中身だけ変えます。SetWasmValueというメッセージで処理をする事にします。

```rust:lib.rs
use enclose::enc; // 宣言部分追記

fn create_closures_for_js(app: &App<Msg, Model, Node<Msg>>) -> Box<[JsValue]> {
    let set_wasm_value = wrap_in_permanent_closure(enc!((app) move |newvalue| {
        app.update(Msg::SetWasmValue(newvalue));
    }));

    vec![set_wasm_value].into_boxed_slice()
}
```

SetWasmValueメッセージの実装をします。

```rust:lib.rs
enum Msg {
    Increment,
    SetWasmValue(i32), // 追記
}

// `update` describes how to handle each `Msg`.
fn update(msg: Msg, model: &mut Model, _: &mut impl Orders<Msg>) {
    match msg {
        Msg::Increment => model.counter += 1,
        Msg::SetWasmValue(input_val) => { // 追記
            model.counter = input_val;
        },
    }
}
```

### 実行

```console
cargo make build_release
cargo make serve
```

で実行し、[ローカルエンドポイント](http://localhost:8000/)へアクセスします。「serWasmValue」ボタン（html側で記述）を押す事で、WebAssembly側（seedの仮想DOMで生成）でのボタンのキャプションが、javaScript側でベタ指定してWebAssembly側に渡される値`123`で更新されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866006/f145787b-030c-8bb2-4157-f54fc5f84843.png)

### 残課題（返り値）

WebAssembly側実行の戻り値も取得したいと思いましたが、前述の一旦理解を諦めた部分で、ちょっと頑張ってみましたが、Closure::new部分で返り値がある関数は受け付けない様なエラーが出ていました。
~~後述WebAssembly側からjavaScriptの関数を呼び出すが実現出来たらコールバック的に実現できそうなので、この部分は一旦諦めます。~~
今ある情報では、関数オブジェクトをjavaScript側に返す時、その関数の返り値を設定するのが難しそうです。どうしてもという場合にはコールバックを使う事にして、主目的であるWebAssembly側関数の返り値の受け取り確認は別方法で行います。

### シンプルな関数で返り値処理（追記）

良く考えたら、start関数は、javaScript側から呼び出され、関数として返り値を戻していました。seedのレンダリングとか考えなければ、最初に確認したrust_fom_jsの方法で出来そうです。

```rust:lib.rs
// 宣言部追記
use std::cell::RefCell;

// 保持用変数宣言
thread_local!(static SAMPLE: RefCell<i32> = RefCell::new(456));

// javaScript側で使用する関数宣言
#[wasm_bindgen]
pub fn wasm_function(inputnumber: i32) -> i32 {
    log(format!("wasm_function: {}", inputnumber));
    SAMPLE.with(|sample_cell| sample_cell.borrow().clone())
}
```

```javascript:index.js
// 追記
window.wasmFunction = (inputnumber) => {
  console.log("js-wasmFunction:" + wasm_function(inputnumber));
}
```

```index.html
<body>
    <section id="app"></section>
    <button onclick="setWasmValue(123)">setWasmValue</button>
    <button onclick="wasmFunction(789)">execWasmFunction</button>
    <script src="/public/index.js" type="module"></script>
</body>
```

「execWasmFunction」ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866006/c45756db-c7aa-9c35-a961-c957cd438380.png)

無事にWebAssembly側に引数の値が渡され「"wasm_function: 789"」のログ出力が行われ、その返り値としてベタで返している456がjavaScript側に返却され「js-wasmFunction:456」のログ出力が行われました。

## WebAssembly側からjavaScriptの関数を呼び出す

### javaScript関数参照

jsFunctionという名前の関数をjavaScript側に作成する予定です。宣言を追記します。

```rust
#[wasm_bindgen]
extern "C" {
    fn jsFunction(i32) -> i32;
}
```

### WebAssembly側でjavaScript関数実行部分実装

update関数内、Msg::Increment処理部分を改造します。WebAssembly側で生成されている最初からあったカウントアップボタンです。
従来のカウントアップに加えて、jsFunctionを、モデルの値を引数に実行します。
戻ってきた値をログ出力する様にします（※wasmモジュールで出力されるので、ブラウザで出力される）。

```rust:lib.rs
        Msg::Increment => {
            model.counter += 1;
            log(format!("wasm: {}", jsFunction(model.counter))); // 追記
        },
```

### javaScript側の関数定義

```javascript:index.js
window.jsFunction = (newnumber) => {
  console.log(newnumber);
  return newnumber + 1000;
}
```

コンソールに表示するだけにします。また、1000を足した数を返すようにしておきます。

※注意
軽くはまったのですが、この記述は、`init('/pkg/package_bg.wasm')`の前に記述する必要がありました。WebAssembly側で処理する瞬間に関数の定義がされていないと認識されない様で、実行時に以下エラーが出てきました。
> package.js:251 Uncaught Error: jsFunction is not defined

### 実行

同様に起動し、WebAssembly側のボタン（seedの仮想DOMにより生成されたボタン）を押すと、コンソールに数値が表示されました。
javaScriptで出力されたメッセージと、wasm内で出力されたメッセージが出力されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866006/99996e32-c9b7-e7a3-f056-5fd2277772d2.png)


# まとめ

seedなどのUIフレームワークでVueやReactエコシステムにあるリッチUIコンポーネントが出てくるのは時間がかかりそうです。
今回確認した手法を使えば、別のリッチUIコンポーネントと組み合わせてページデザインできると思います。

連携部分を準備するのは大変なので、基本的にseedなどのWebAssembly側フレームワークで作成したものは、ある程度まとまったUIコンポーネントとして使用するのが良さそうです。
あとは、seed側ではUIを一切使用せず処理に特化し、javaScriptでやるには重い処理をさせる（UIはjavaScript側）という感じでしょうか。

WebAssemblyとjavaScriptでの情報やり取りのオーバーヘッドはそれなりにあるという話も聞くので、その意味でもWebAssembly側はまとまった処理をやる様にするのが良いと思います。

今回の件は主目的がRustの勉強がなので、そこらへんは度外視してますが、3D視点処理＋描画をWebAssembly側でやっていて結構それに沿っている気がします。

次はVueかReactを使ってリッチなページにしたいと思っています。

# 参考にさせて頂いたページ

## 公式ページ

[RefCell<T>と内部可変性パターン](https://doc.rust-jp.rs/book-ja/ch15-05-interior-mutability.html)
[クロージャ](https://doc.rust-jp.rs/rust-by-example-ja/fn/closures.html)
[Struct std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html)
[Struct wasm_bindgen::JsValue](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/struct.JsValue.html)

## 個人記事

[Rustのstatic変数とthread local](https://qiita.com/tatsuya6502/items/bed3702517b36afbdbca)
[Rustのスレッドローカル変数について](https://qnighy.hatenablog.com/entry/2017/05/30/070000)
[&str を String に変換する4つの方法](https://qiita.com/uasi/items/3b08a5ba81fede837531)
[Rust の Foreign Function Interface (FFI)](https://qiita.com/termoshtt/items/0fa9959f9eb64b0907e2)