How do I implement js_sys

I'm trying to implement js_sys::Reflect::get, but I'm not sure how.

I've the following code:

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 do I read the fields in Rust with Reflect::get?

js_sys::Reflect::get

That's probably not what you should do. (Anything with "reflection" in it is likely not what you should do most of the time, in fact.)

If you are writing a Rust function, chances are you know the exact layout (names and types of fields) of the object you are receiving. So you could just declare a statically typed struct and use JsValue::into_serde() to convert the dynamic JavaScript value to a value your static type.

What would be a use case of Reflect than?

Try this:

  1. create a Map in js_sys - Rust from the JsValue

  2. call the .get method on the js_sys::Map

  3. to pass a string as a &JsValue, use & "width".into()

In Rust - probably nothing, except if it has to work with somewhat obscure JS API. It was added to js_sys mostly for API completeness, not for some specific use.

1 Like

This might work. Unfortunately I'm struggling making the (obj: JsValue) a map.

I think you need to have the key and value to create it? And in Rust I only have the obj? I'm not sure :s

Can you use Map in js_sys - Rust ?

In particular

impl From<JsValue> for Map
fn from(obj: JsValue) -> Map

Something else worth reading:

In particular:

/// Representation of an object owned by JS.
///
/// A `JsValue` doesn't actually live in Rust right now but actually in a table
/// owned by the `wasm-bindgen` generated JS glue code. Eventually the ownership
/// will transfer into wasm directly and this will likely become more efficient,
/// but for now it may be slightly slow.
pub struct JsValue {
    idx: u32,
    _marker: marker::PhantomData<*mut u8>, // not at all threadsafe
}

I think part of the 'clunkiness' in dealing with the JsValue API is that a JsValue is really a u32, pointing at a table, which stores the actual JS objects.

I see. It's unfortunate haha. I'm quite far off from dealing with all the clunkiness rust/wasm/js has. I continue to run into it and it's near impossible for me to read the documentation and understand - implement it.

Hopefully in 2/3 years or so this will be a bit more intuitive.

I'll continue to read more into your suggestions/examples though, thanks!

Yeah, unfortunately there is no way for WebAssembly to reference JavaScript objects at the moment[1]. To work around this, libraries like wasm-bindgen and emscripten use a bit of extra indirection and glue code to make it look like you are passing around JavaScript objects when you are actually just passing around some sort of token.

There are a couple ways you could implement this. The Reflect-based version you are asking about looks like this:

use js_sys::Reflect;
use wasm_bindgen::JsString;

pub fn initialize_x(obj: JsValue) {
    let key = JsString::from_str("width");
    let width = Reflect::get(&obj, &key).unwrap();
    console::log_1(&width);
}

However, if I had a choice, I would prefer to use wasm-bindgen's getter/setter support:

#[wasm_bindgen]
extern "C" {
 type Rectangle;

  #[wasm_bindgen(getter)]
  fn width(this: &Rectangle) -> f32;
}

pub fn initialize_x(obj: Rectangle) {
    let width: f32 = obj.width();
    console::log_1(&JsValue::from_f32(width));
}

  1. it requires interacting with the GC and a bunch of other proposals that are yet to be accepted/implemented. ↩ī¸Ž

2 Likes

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.