wasm-bindgen icon indicating copy to clipboard operation
wasm-bindgen copied to clipboard

Spread operator doesn't work with objects created in Rust code

Open maxence-cornaton opened this issue 1 year ago • 2 comments

Describe the Bug

Sometimes, we operate with JS code using the spread operator. If the object has been defined in JS, then spreading it makes all of its properties available to the new object. If the object has been defined in Rust, then spreading it doesn't work because the effective object is just a pointer to the object in WASM memory.

E.g. with the following:

#[wasm_bindgen]
pub struct Foo {
    id: String
}

#[wasm_bindgen]
impl Foo {
    #[wasm_bindgen(constructor)]
    pub fn new(id: String) -> Self {
        Self {
            id
        }
    }

    #[wasm_bindgen(getter)]
    pub fn id(&self) -> String {
        self.id.clone()
    }
}
let foo = new Foo("1);
console.log(foo.id);    // 1
let bar = {...foo};
console.log(bar.id};    // undefined

However, if the object is defined directly in JS, bar.id would be 1:

class Foo {
  constructor(id) {
    this.id = id;
  }
}

let foo = new Foo("1");
console.log(foo.id)    // 1
let bar = {...foo};
console.log(bar.id);   // 1

Expected Behavior

The spread operator should expand properties of objects defined in Rust.

Actual Behavior

The spread operator doesn't expand properties of objects defined in Rust.

Additional Context

I'm working with Flowbite lib. I'm trying to create an Accordion from within my Rust code. This mainly work, but I can't pass options as actual options are expanded from defaults and parameter, using the spread operator. Note: I have not found any way to use AccordionOptions defined in Flowbite as this doesn't seem to be exported, so I have defined my own based on the example written in the first section of this bug report.

maxence-cornaton avatar Mar 21 '25 12:03 maxence-cornaton

After some time, I have found a workaround: instead of defining the struct in Rust code, it can be defined in JS and bound to Rust code, such as follows:

class AccordionOptions {
    constructor(alwaysOpen) {
        this.alwaysOpen = alwaysOpen;
    }
}
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen]
    type AccordionOptions;

    #[wasm_bindgen(constructor)]
    fn new(#[wasm_bindgen(js_name = "alwaysOpen")] always_open: bool) -> AccordionOptions;
}

As I strive to use as less JS as possible, this isn't perfect. But at least, there is a solution to customise my accordion :)

maxence-cornaton avatar Mar 21 '25 13:03 maxence-cornaton

I believe you should be able to annotate your struct with wasm_bindgen(inspectable), no?

demhadais avatar Apr 30 '25 19:04 demhadais