How do I sent a JS object to Rust, through WASM?

JS code:

let x = {
	width: 10,
	height: 10,
};
// Rust func
initialize_x(x);

Rust code:

#[wasm_bindgen]
pub fn initialize_x(obj: JsValue) {
    console::log_1(&obj.width.into()); // error, field doesn't exist.
}

How can I read fields?

// Using: wasm-pack build --target web

I think you should use something like

#[derive(serde::Deserialize)]
struct MyObj {
    width: u32,
    height: u32,
}

let obj: MyObj = JsValue::from_serde(obj).unwrap();

Note that for this to work you need to add a dependency on serde and enable the serde-serialize feature of wasm-bindgen like so:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }

See Arbitrary Data with Serde - The `wasm-bindgen` Guide for more info.

1 Like

I see. Do you know what this does than? https://rustwasm.github.io/wasm-bindgen/contributing/design/js-objects-in-rust.html

What exactly do you mean?

You can send object to Rust, as given in that example in the link. But not sure what else they describe, since you can't actually read the fields. So I don't really understand the point.

If you do

#[derive(serde::Deserialize)]
struct MyObj {
    width: u32,
    height: u32,
}

let obj: MyObj = JsValue::from_serde(obj).unwrap();

you get a regular value of type MyObj you can read from.

What they describe in JS Objects in Rust - The `wasm-bindgen` Guide is how passing javascript values to wasm works. JsValue is an example which is relatively opaque except for methods like from_serde, but if you have a specific builtin type (and not a generic Object like in your case) you can use for example web-sys to interface with it. web-sys uses wasm-bindgen.

The docs do not currently make this very discoverable, but you do also have the option of using js_sys::Reflect::get to do the equivalent of JavaScript’s obj[“width”] which obj.width is syntax sugar for.

1 Like

Can you tell me how I implement this in code? I'm pretty bad with it :S

Here's some quick examples of using Reflect::get and the related Reflect::set.

use wasm_bindgen::JsValue;
use js_sys::{Object, Reflect, TypeError};

fn main() {
    let width_key = JsValue::from("width");
    let height_key = JsValue::from("height");

    // Create an example object
    let obj = Object::new();

    assert_eq!(
        Reflect::set(&obj, &width_key, &JsValue::from(10)),
        Ok(true)
    );
    assert_eq!(
        Reflect::set(&obj, &height_key, &JsValue::from(20)),
        Ok(true)
    );

    // Read the width from the example object.
    assert_eq!(
        Reflect::get(&obj, &width_key),
        Ok(JsValue::from(10))
    );

    // Examples of things going wrong
    let misspelled_key = JsValue::from("qidth");

    assert_eq!(
        Reflect::get(&obj, &misspelled_key),
        Ok(JsValue::undefined())
    );

    let bad_output = Reflect::get(&JsValue::undefined(), &misspelled_key);

    assert!(
        bad_output.is_err()
    );

    assert!(
        bad_output.unwrap_err().is_instance_of::<TypeError>()
    );
}

(Note: I have confirmed this code compiles but not actually confirmed that it ran correctly. In particular I'm only guessing at the result of intentionally incorrect cases, because the generated documentation is not very clear on this.)

I've now had a chance to test this, and have updated the code so that all the asserts pass.

2 Likes

Also have a look at GitHub - cloudflare/serde-wasm-bindgen: Native integration of Serde with wasm-bindgen which may be the replacement of the current implementation.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.