web-dev-qa-db-ja.com

オブジェクトの配列をWebAssemblyに渡し、wasm-bindgenで構造体のベクトルに変換する方法は?

次のような整数の配列を渡すことが可能です。

const js = import("./webassembly_Rust");
let array_nums = [1,2,3,4,5,6,7,8,9];

js.then(js => {
  js.test( array_nums );
}); 

webAssemblyに追加し、次のようなベクターに保存します。

extern crate serde_json;
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[macro_use]
extern crate serde_derive;

#[wasm_bindgen]
pub fn test(array: JsValue) {
    let elements: Vec<u32> = array.into_serde().unwrap();
}

次のように単一のオブジェクトを渡すことも可能です。

const js = import("./webassembly_Rust");
let jsObject = {name: "hello world", id: "99", parent_id: "11"};

js.then(js => {
  js.test( jsObject );
}); 

webAssemblyに次のようにElement構造体として保存します。

extern crate serde_json;
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
pub struct Element {
    name: String,
    id: String,
    parent_id: String,
}

#[wasm_bindgen]
pub fn test(js_object: &JsValue) {
    let element: Element = js_object.into_serde().unwrap();
}

次に試みたのは、次のようなオブジェクトの配列を渡すことです。

const js = import("./webassembly_Rust");
let arrayOfObjects = [
  {name: "hello world", id: "99", parent_id: "88"},
  {name: "hello world2", id: "88", parent_id: "12"},
  {name: "hello world3", id: "77", parent_id: "88"}
]

js.then(js => {
  js.test( arrayOfObjects );
}); 

webAssemblyに追加し、次のようなElement構造体のベクトルとして保存します。

extern crate serde_json;
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
pub struct Element {
    name: String,
    id: String,
    parent_id: String,
}

#[wasm_bindgen]
pub fn test(js_objects: &JsValue) {
    let elements: Vec<Element> = js_objects.into_serde().unwrap();
}

これはコンパイルされますが、このコードを実行するとエラーが発生します。

func $__Rust_start_panic (param i32) (result i32)
  unreachable
  unreachable
end

screenshot_promise_rejection_error

次のような数値で満たされたオブジェクトの配列を渡します。

const js = import("./webassembly_Rust");
let arrayOfNumObjects = [
    {name: 1, id: 2, parent_id: 3 },
    {name: 1, id: 2, parent_id: 3 },
    {name: 1, id: 2, parent_id: 3 }
]

js.then(js => {
  js.test( arrayOfNumObjects );
}); 

Element構造体にu32値のみが含まれている場合、WebAssemblyへの変換が可能です。

extern crate serde_json;
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
pub struct Element {
    name: u32,
    id: u32,
    parent_id: u32,
}

#[wasm_bindgen]
pub fn test(js_objects: &JsValue) {
    let elements: Vec<Element> = js_objects.into_serde().unwrap();
}

String構造体のElementタイプが原因で問題が発生しているようです。

何を間違えたのですか?

次の記事を見つけましたが、問題の解決策が見つかりません。

  • Serdeを使用したJsValueへの任意のデータのシリアライズおよびデシリアライズ

    これは、JavaScriptオブジェクトを構造体に変換する方法を説明しますが、オブジェクトの配列を構造体のベクトルに変換する方法は説明しません。

  • js_sysクレート

    このクレートでは、Rustの配列やオブジェクトなどのJavaScriptタイプを使用できますが、これは私が望んでいることではありません。 JavaScriptの値をRust対応するものに変換したいのです。このクレートでは、RustでインラインでJavaScriptを使用することしかできません...私が理解している限り、これはRustだけを使用するほど高速ではありません。 。

  • githubの問題

11
Gregor

指示に従って basic Rust/WASM setup を取得し、次に Serdeを介した任意のデータ のサポートを追加します。

数値を返すようにコードを変更し、その数値を出力して、機能していることを確認しました。

Cargo.toml

[package]
name = "ww"
version = "0.1.0"
authors = ["An Devloper <[email protected]>"]
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
serde_json = "1.0.32"
serde_derive = "1.0.80"
serde = "1.0.80"

src/lib.rs

extern crate serde_json;
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
pub struct Element {
    name: String,
    id: String,
    parent_id: String,
}

#[wasm_bindgen]
pub fn test(js_objects: &JsValue) -> i32 {
    let elements: Vec<Element> = js_objects.into_serde().unwrap();
    elements
        .iter()
        .map(|e| {
            let id = e.id.parse::<i32>().unwrap_or(0);
            let parent_id = e.parent_id.parse::<i32>().unwrap_or(0);
            id + parent_id
        })
        .sum()
}

index.js

const js = import("./ww");

let arrayOfObjects = [
  { name: "hello world", id: "99", parent_id: "88" },
  { name: "hello world2", id: "88", parent_id: "12" },
  { name: "hello world3", id: "77", parent_id: "88" },
]

js.then(js => {
  const sum = js.test(arrayOfObjects);
  console.log(sum);
});

package.json

{
  "scripts": {
    "serve": "webpack-dev-server"
  },
  "devDependencies": {
    "html-webpack-plugin": "^3.2.0",
    "webpack": "^4.0.1",
    "webpack-cli": "^3.1.1",
    "webpack-dev-server": "^3.1.0"
  }
}

webpack.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({
      title: "Getting started with WASM"
    })
  ],
  mode: "development"
};

次に実行します:

# once
npm install
# every time the code changes
cargo +nightly build --target wasm32-unknown-unknown
wasm-bindgen target/wasm32-unknown-unknown/debug/*.wasm --out-dir .
npm run serve

WASM対応のブラウザーでページにアクセスします。


注意深い読者は、私がOPと何も何もしなかったことに気付くでしょう。これは、このコードが既にそのまま機能しているためです。 Rustコードを変更するたびに、次のことを確認してください。

  1. Rustコードを作成する
  2. Wasm-bindgenを再実行
7
Shepmaster