Search code examples
rustwasm-bindgen

How do I ensure the right HTML element is passed from JavaScript to my Rust function?


If I create a Rust function with wasm_bindgen that accepts an HtmlCanvasElement, how do I make sure it fails when it gets to the Rust side?

JavaScript:

(async () => {
    const demo = await import('./pkg/demo').catch(console.error);
    demo.setCanvas('Hello Element!');  
})();

Rust:

use wasm_bindgen::prelude::*;
use web_sys::{console, HtmlCanvasElement};

#[wasm_bindgen]
pub fn setCanvas(canvas: &HtmlCanvasElement) {
    //  Why does this even get here? I didn't pass in an HtmlCanvasElement
    console::log_1(&canvas);
}

It looks like I'm getting the type HtmlCanvasElement but if I try to use it as an HtmlCanvasElement, it doesn't have the functions because I'm passing in a string instead of the actual canvas element. I want it to fail when I set it, not at some later time when I try to use it.


Solution

  • There's not much type control at the boundary between the DOM and Rust in wasm_bindgen. The JavaScript part handles what is seen from Rust as JsValue.

    It means you have to do the checked conversions yourself (or let a future lib or framework do it for you).

    The JsCast trait helps for this. It lets you write this for example:

    #[wasm_bindgen]
    pub fn setElement(canvas: JsValue) -> Result<(), JsValue> {
        match canvas.dyn_into::<HtmlCanvasElement>() {
            Ok(canvas) => {
                // ...
    
                Ok(())
            }
            Err(_) => Err(JsValue::from_str("argument not a HtmlCanvas")),
        }
    }