Rust+wasmでJSONを扱う


Rust+wasmで複雑なものを作ろうとするとJSON形式のデータは避けて通れないと思いますが、wasm-bindgenのドキュメントにあるJsValueを使うのがよさそうです。

Cargo.toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
lib.rs
use wasm_bindgen::prelude::*;
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
pub struct Monster {
    name: String,
    value: String
}

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);
    pub fn console_log(s: &str);
}

#[wasm_bindgen]
pub fn return_name(val: &JsValue){
    let m: Monster = val.into_serde().unwrap();
    //alert(&m.name);
    console_log(&m.name);
    console_log(&m.value);
}

dependenciesでwasm-bindgenのバージョンだけでなくserde-seriarizeというfeatureも指定する必要があります。(これを読み飛ばしてて、気付くまで時間かかった・・・)

return_nameの引数のval: &JsValueが今回のキモです。今回はinto_serde()でJSONを読み込んでますが、逆にRustの値をJSONにするfrom_serde()というメソッドもサポートしてます。

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>hello-wasm example</title>
  </head>
  <body>
    <script type="module">
        import * as mod  from "./wasm.js";
        let monster = {
          "name":"pikachu",
          "value":"100"
        };
        (async () => {
            await mod.default();
            mod.return_name(monster);
        })();
    </script>
    <script>
      const console_log = (val) => {
        console.log(val);
      }
    </script>
  </body>
</html>

次にJavaScript側です。wasm.jsから読み込んだRustの関数(return_name)にmonsterという値をそのまま渡します。

絵にすると、こんな感じです。Rustで書いたreturn_nameという関数をJavaScriptのasyncの中で呼び出し、そのreturn_nameではJavaScriptで書いたconsole_logという関数を呼び出しています。

これでJavaScriptで生成した大量のデータをwasmで処理できますね。

環境のインストールとビルドは、こちらの記事が参考になると思います。