rustwasm / wasm-bindgen

Facilitating high-level interactions between Wasm modules and JavaScript
https://rustwasm.github.io/docs/wasm-bindgen/
Apache License 2.0
7.84k stars 1.08k forks source link

FromWasmAbi is not implemented for fn(SomeStruct) , while SomeStruct is #[wasm_bindgen] #1092

Open venil7 opened 5 years ago

venil7 commented 5 years ago

Basically I have a 3 structs and two types defined

type OnMoveEvent = fn(Board) -> ();
type OnGameOverEvent = fn(Player) -> ();

#[wasm_bindgen]
pub struct Game {
  on_move: OnMoveEvent,
  on_game_over: OnGameOverEvent,
  board: Board,
}
#[wasm_bindgen]
pub struct Board { .. }
#[wasm_bindgen]
pub struct Player { .. }

All 3 structs are wasm_bindgen and types can not be marked as wasm_bindgen. However types are just functions accepting structs and returning void

When I add wasm_bindgen to Game impl I get the following error

 --> src/game.rs:16:1
     |
  16 | #[wasm_bindgen]
     | ^^^^^^^^^^^^^^^ the trait `wasm_bindgen::convert::traits::FromWasmAbi` is not implemented for `fn(board::Board)`

That is because new has the following signature

  pub fn new(on_move: OnMoveEvent, on_game_over: OnGameOverEvent) -> Game {

It seemed to me that it should be simple to translate types as they are functions accepting wasm_bindgen structs, but it appears as it's not the case

Is this a bug or am I missing something

full code is here

TitanThinktank commented 5 years ago

anyone getting error: failed to extract wasm-bindgen custom sections ?

limira commented 5 years ago

It seems that you want to pass some JS functions to RS via:

pub fn new(on_move: OnMoveEvent, on_game_over: OnGameOverEvent) -> Game {

I can think you may want to use js closure instead of using Rust's fn.

If it is not your intention, move fn new into another impl block without wasm-bindgen.

venil7 commented 5 years ago

Yes, indeed I do want to pass JS functions. Thanks for pointing out js_sys::Function , it looks like javascript functions seen from Rust are signature-less ? No way to control types passed and returned, like I was trying in the above code?

limira commented 5 years ago

js_sys::Function has call0, .., call3 for you to pass 0 or up to 3 arguments to the underlying JS functions.

I see your Board and Player are Rust's types. Why don't you define Rust's methods for them and call them directly from Rust to Rust code? For example:

impl Board {
    fn move(&mut self) {
        //
    }
}
impl Player {
    fn game_over(&self) {
        //
    }
}
impl Game {
    pub fn make_move(...) -> Result<...> {
        //...
        if self.board.state().game_over {
            self.board.state().winner.game_over();
        } else {
            self.board.move();
        }
        //...
    }
}

Personally, I will try to avoid calling cross boundary (JS <-> RS) as much as I can.

If you need a better answer, you may have to wait for more experienced guys to come back from their weekend.

alexcrichton commented 5 years ago

Thanks for the report @venil7!

@limira is correct here in that you can't pass in a fn(X) -> Y from JS, but rather to take a JS closure you'll need to take a js_sys::Function (or an opaque JsValue)

This was originally tracked by https://github.com/rustwasm/wasm-bindgen/issues/103 but with Function I figured it was "closed enough". We could probably shore up the documentation in this regard though!