All posts

WebAssembly / Working with wasm-bindgen

Posted On 01.28.2022

wasm-bindgen is an awesome tool made by rustwasm team, make it very easy to expose Rust’s data/functions to JavaScript and vice versa.

To use, you can add a #[wasm_bindgen] as an annotation the code where you want to be exposed or import to/from JavaScript, for example:

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}
 
#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}

With the above code, we import the alert() function from JavaScript to Rust, and expose Rust’s greet() function to JavaScript.

A Rust struct will be exposed as a class in JavaScript:

#[wasm_bindgen]
pub struct Foo {
    contents: u32,
}
 
#[wasm_bindgen]
impl Foo {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Foo {
        Foo { contents: 0 }
    }
}

You can import an ES6 module to use in Rust:

#[wasm_bindgen(module = "./bar")]
extern "C" {
    fn blah(bleh: &JsValue);
}
 
// blah() function is in bar.js

There are two attributes you need to know about, js_namespace: indicates the JavaScript type for your binding, and js_name for the function name. We can use them to import multiple signatures of a polymorphic JavaScript function, like this:

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console, js_name = log)]
    fn log_str(s: &str);
    
    #[wasm_bindgen(js_namespace = console, js_name = log)]
    fn log_u32(n: u32);
}

Using js_name, you can rename not just function, but also classes or types, for example:

#[wasm_bindgen]
extern "C" {
    // Import JavaScript's String type as JsString in Rust
    #[wasm_bindgen(js_name = String)]
    type JsString;
}
 

And if you’re renaming type names, you need to use js_class attribute when binding a function of that type:

#[wasm_bindgen(method, js_class = "String", js_name = charAt)]
fn char_at(this: &JsString, index: u32) -> JsString;

The web_sys crate also provides some good binding from JS:

use web_sys::console;
 
console::log_1(&"Hello using web-sys".into());

Finnaly, use wasm-pack to build your Rust project into a WebAssembly module, then use it in your JavaScript project.

wasm-pack build

If you’re using TypeScript or NodeJS, you might want to take a look at wasm-bindgen command line tool https://rustwasm.github.io/wasm-bindgen/reference/cli.html

If you’re working with various data type/struct, it’s very helpful to use serde for serializing/deserializing into and from JS. See more at https://rustwasm.github.io/wasm-bindgen/reference/arbitrary-data-with-serde.html