Wasm_bindgen for Boxed structs

Hi,

Relatively new to Rust and I've been working with wasm_bindgen for a project. I need to be able to generate javascript for a struct I've defined in rust but get an error decorating it with

#[wasm_bindgen]

The error messages I'm seeing are,

the trait bound `std::boxed::Box<ListNode>: wasm_bindgen::convert::OptionIntoWasmAbi` is not satisfied

the trait `wasm_bindgen::convert::OptionIntoWasmAbi` is not implemented for `std::boxed::Box<ListNode>`

help: the following implementations were found:
        <std::boxed::Box<[T]> as wasm_bindgen::convert::OptionIntoWasmAbi>
        <std::boxed::Box<[f32]> as wasm_bindgen::convert::OptionIntoWasmAbi>
        <std::boxed::Box<[f64]> as wasm_bindgen::convert::OptionIntoWasmAbi>
        <std::boxed::Box<[i16]> as wasm_bindgen::convert::OptionIntoWasmAbi>
      and 10 others
note: required because of the requirements on the impl of `wasm_bindgen::convert::IntoWasmAbi` for `std::option::Option<std::boxed::Box<ListNode>>`rustc(E0277)
lib.rs(41, 1): the trait `wasm_bindgen::convert::OptionIntoWasmAbi` is not implemented for `std::boxed::Box<ListNode>`

and

the trait bound `std::boxed::Box<ListNode>: std::marker::Copy` is not satisfied

the trait `std::marker::Copy` is not implemented for `std::boxed::Box<ListNode>`

note: required because of the requirements on the impl of `std::marker::Copy` for `std::option::Option<std::boxed::Box<ListNode>>`rustc(E0277)

lib.rs(41, 1): required by this bound in `__wbg_get_listnode_next__const::__wbg_get_listnode_next::assert_copy`

lib.rs(45, 15): the trait `std::marker::Copy` is not implemented for `std::boxed::Box<ListNode>`

The implementation for the struct looks like...

#[wasm_bindgen]
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ListNode {
    pub val: i32,
    pub next: Option<Box<ListNode>>,
}

#[wasm_bindgen]
impl ListNode {
    #[inline]
    fn new(val: i32) -> Self {
        ListNode { next: None, val }
    }
}

How can I go about accomplishing this?

Thanks!

You can't put #[wasm_bindgen] on structs that are not Copy because of limitations in the way JS and WASM can talk to each other.

(This struct cannot be Copy - you would have to translate it into something else if you want to send it over the JS/WASM boundary. For example you could use serde for this.)

1 Like

I don't know much about wasm_bindgen-specific issues but if you were generating a C interface to your Rust code with bindgen you would never directly expose types like Box. The superficial issue is that Box isn't #[repr(C)] but the deeper issue is that the foreign language doesn't know how to respect Rust's lifetime and value semantics, so there can't be a generic way to automatically interoperate for types like this. When you need to do similar things in a C interface to Rust code, you would manually design and implement a handle or pointer based interface for bridging the two worlds safely and correctly.

1 Like

Box has a guaranteed layout.

1 Like

You are a life-saver! I ended up using serde like you suggested and it worked like a charm.

My rust side ended up looking like...

#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub struct ListNode {
    pub val: i32,
    pub next: Option<Box<ListNode>>,
}

impl ListNode {
    #[inline]
    pub fn new(val: i32) -> Self {
        ListNode { next: None, val }
    }
}


#[wasm_bindgen]
pub fn run_do_stuff_from_js(l1_js: &JsValue, l2_js: &JsValue) -> JsValue{
    let l1: Option<Box<ListNode>> = Some(Box::new(l1_js.into_serde().unwrap()));
    let l2: Option<Box<ListNode>> = Some(Box::new(l2_js.into_serde().unwrap()));

    let result = do_stuff(l1, l2);
    JsValue::from_serde(&result).unwrap()
}

JS side -

let firstLl = {
               val: 2,
               next: {
                   val: 4,
                   next: {
                       val: 3,
                       next: null
                   }
               } 
            };
            
            let secondLl = {
                val: 5,
                next: {
                    val: 6,
                    next: {
                        val: 4,
                        next: null
                    }
                }
            };
        let sol = wasm.run_do_stuff_from_js(firstLl, secondLl);