Rust wasm-bindgen struct with string
Asked Answered
R

2

8

I'm trying to export the following struct:

#[wasm_bindgen]
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum TokenType {
    KeywordLiteral,
    NumberLiteral,
    Operator,
    Separator,
    StringLiteral,
}

#[wasm_bindgen]
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct Token {
    pub typ: TokenType,
    pub val: String,
}

but I'm getting:

error[E0277]: the trait bound `token::TokenType: std::marker::Copy` is not satisfied
  --> src\tokenizer\token.rs:17:14
   |
14 | #[wasm_bindgen]
   | --------------- required by this bound in `__wbg_get_token_typ::assert_copy`
...
17 |     pub typ: TokenType,
   |              ^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `token::TokenType`

as well as:

error[E0277]: the trait bound `std::string::String: std::marker::Copy` is not satisfied
  --> src\tokenizer\token.rs:18:14
   |
14 | #[wasm_bindgen]
   | --------------- required by this bound in `__wbg_get_token_val::assert_copy`
...
18 |     pub val: String,

I can add #[derive(Copy)] to TokenType but not to String.

I'm new to rust so help is really appreciated.

Rojo answered 4/7, 2021 at 10:48 Comment(0)
E
12

According to wasm-bindgen#1985, public fields of structs are required to be Copy in order for the automatically generated accessors to function.

You can either make the fields private, or annotate them with #[wasm_bindgen(skip)], and then implement a getter and setter directly.

There is an example provided in wasm-bindgen's docs describing how to write these:

#[wasm_bindgen]
pub struct Baz {
    field: i32,
}

#[wasm_bindgen]
impl Baz {
    #[wasm_bindgen(constructor)]
    pub fn new(field: i32) -> Baz {
        Baz { field }
    }

    #[wasm_bindgen(getter)]
    pub fn field(&self) -> i32 {
        self.field
    }

    #[wasm_bindgen(setter)]
    pub fn set_field(&mut self, field: i32) {
        self.field = field;
    }
}

When objects are shared between wasm and js, the js object only contains the pointer to the struct inside the wasm runtime's memory. When you access fields from JS, it goes through a defined property which makes a function call to the wasm code asking it for the property value (you can think of the wasm memory as a bit UInt8Array).

Requiring the objects to be Copy is probably done to avoid surprises when auto-generating these getters and setters. If you implement them manually, you can have the same behaviour in JS and be able to control what's being set on the rust side.

Entasis answered 4/7, 2021 at 15:1 Comment(7)
So it's not possible to simply have a String in a struct?Rojo
And how will that compile to typescript?Rojo
I edited the answer to clarify. You are free to have strings in structs, these just can't be pub if you want to use the auto-generated accessors.Entasis
So you are telling me I can have a getter and setter but not directly expose a String to JavaScript?Rojo
Ah I just read through your post again. I see, this is pretty cool, although the issue is that rust really limits what I can actually return from wasm. E.g. I want to return a list of tokens but rust simply does not allow vectors to be returned (even though they behave really similar to JS Arrays). So currently my code is serialized via serd and thus copied anyway.Rojo
If you have the time, you could take a look at another one of my questions, would be highly appreciated :) #68245835Rojo
Vectors aren't suppored but Box<[JsValue]> is, and you can use Vec::into_boxed_slice to convert it from a Vec<JsValue>, which you get from the wasm-bindgen generated From impl, using something like .map(JsValue::from).Entasis
E
0

wasm-bindgen introduced the getter_with_clone attribute to address this issue.
You can use it with the macro, so your structs becomes :

#[wasm_bindgen(getter_with_clone)]
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum TokenType {
    KeywordLiteral,
    NumberLiteral,
    Operator,
    Separator,
    StringLiteral,
}

It will compile and it's less verbose than the other solutions.

Elberfeld answered 6/8 at 15:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.