Access JavaScript API with Rust
Tags: rust, webassembly, javascript, webdev
Published: 2020-01-04 21:28:14



![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/yygcr372zo1lyg32mmv1.gif)

JavaScript is everywhere. It enabled billions of people to develop and deliver projects. JavaScript is easy to get started. It is for a reason, JavaScript is one of the highly used programming language. 

It is true that there are weird corners in the language. But believe me every programming language out there has those corners. Check this talk [here](https://www.destroyallsoftware.com/talks/wat).

Javascript is a dynamically typed language. It is one of its main advantage as well as disadvantage. Dynamic typing makes JavaScript API easy to write and understand. That is contextually simpler for people. But during compilation, the compiler has to do the hard work. This often leads to runtime exception and unpredictable performance.

Being a widely used language, JavaScript comes with a lot of bells and whistles. These features provides an elegant API (not talking about [smooshmap](https://developers.google.com/web/updates/2018/03/smooshgate)). JavaScript provides a rich API to work with `Objects`, `Arrays`, `Maps`, `Sets`, and others. 

---

WebAssembly provides strict typing and predictable performance. The performance is much faster than compared with JavaScript. Refer 👇

{% link https://dev.to/sendilkumarn/increase-rust-and-webassembly-performance-382h %} 

But WebAssembly is not always faster, there are scenarios where JavaScript is faster than the WebAssembly module. For example, to access a DOM JavaScript is much faster than compared with WebAssembly module. The boundary crossing has an impact. During those times it is great to use JavaScript to have higher performance.

---

Check out my book on Rust and WebAssembly [here](https://sendilkumarn.com/wasm-book)

---

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/aoup8pb1xz96p0ldhslc.gif)

JavaScript and WebAssembly needs to work closely in an application. The JavaScript engine needs to provide seamless integration between the JavaScript and WebAssembly. Check out [here](https://hacks.mozilla.org/2018/10/calls-between-javascript-and-webassembly-are-finally-fast-🎉/) about how Firefox made calls between JavaScript and WebAssembly faster.

For a seamless integration between JavaScript and WebAssembly, it is important that both should understand each other. The JavaScript should provide the necessary context to enable languages like Rust too interoperate. But it is a tedious process to write the necessary bindings between JavaScript and Rust. Handcrafting the bindings is a mundane process. 

But what if we have bindings to those APIs, common API, that is present in both `Node.js` and `Browser` environment.

The rustwasm team's answer to that is the `js-sys` crate.

---

[JS-Sys Crate](https://rustwasm.github.io/wasm-bindgen/api/js_sys/)

> The `js-sys` crate contains raw `#[wasm_bindgen]` bindings to all the global APIs guaranteed to exist in every JavaScript environment by the ECMAScript standard. - RustWASM

The `js-sys` crate provide bindings to the JavaScript's standard built-in objects, including their methods and properties.

---

## Write some code ✍️

Create a default project with cargo new command.

```bash
$ cargo new --lib jsapi
```

Please copy over the `package.json`, `index.js`, and `webpack.config.js` from the previous [post](https://dev.to/sendilkumarn/rust-and-webassembly-for-the-masses-wasm-bindgen-57fl). 

Change the contents of `Cargo.toml`:

```toml
[package]
name = "jsapi"
version = "0.1.0"
authors = ["Sendil Kumar <sendilkumarn@live.com>"]
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2.56"
js-sys = "0.3.33"
```

We added `js-sys = "0.3.33"` as a dependency. Now open the `src/lib.rs` and replace the file with the following contents. 

```rust
use wasm_bindgen::prelude::*;

use js_sys::Map;

#[wasm_bindgen]
pub fn new_js_map() -> Map {
    Map::new()
}
```

We imported the `wasm_bindgen` library. Then imported `map` from the `js_sys` crate. The `js_sys` crate provides all the necessary JavaScript API information. 

In the function `new_js_map` we return a Map of type `js_sys::Map`. The js_sys crate is responsible for defining the type information for Rust and providing all the APIs. To create a new Map we simply call `Map::new()`.

That is it, we created a JavaScript Map inside Rust. Now we can access this map inside the Rust and pass it to the JavaScript world as a JavaScript Map.

> Note that In Rust, return keyword is optional. The last line without a semicolon is considered a return statement.

---

Then we create a function to create a Map, set values into the map and retrieve it. 

```rust
#[wasm_bindgen]
pub fn set_get_js_map() -> JsValue {
    let map = Map::new();
    map.set(&"foo".into(), &"bar".into());
    map.get(&"foo".into())
}
```

We created a function `set_get_js_map`, it is annotated with `#[wasm_bindgen]`. It returns `JSValue`. This is a wrapper used by Rust for specifying the JavaScript values. The `JSValue` type is defined in the `js_sys` crate.

> JsValue is a representation of an object owned by JS. A JsValue doesn't actually live in Rust right now but actually in a table owned by the `wasm-bindgen` generated JS glue code. Eventually the ownership will transfer into wasm directly and this will likely become more efficient, but for now it may be slightly slow.

We are creating a new map using the Rust syntax. We set the value into the map. Instead of simply accepting String type, the `map.set` or `map.get` accepts a pointer to the `JsValue`. Rust provides value to value converter `into` function, that converts the value from Rust's str type into the JsValue type. 

Finally we are getting the value from the map using `map.get` function call. This returns "bar" as the output as a JavaScript Value (`JsValue`).

We can run through the map using `foreach` inside the Rust code like below:

```rust
#[wasm_bindgen]
pub fn run_through_map() -> f64 {
    let map = Map::new();
    map.set(&1.into(), &1.into());
    map.set(&2.into(), &2.into());
    map.set(&3.into(), &3.into());
    map.set(&4.into(), &4.into());
    map.set(&5.into(), &5.into());
    let mut res: f64 = 0.0;

    map.for_each(&mut |value, _| {
        res = res + value.as_f64().unwrap();
    });

    res
}
```

This creates a map and then loads the map with values 1, 2, 3, 4, 5. Then runs over the created map and adds the value together. This produces an output of "15" (i.e., 1 + 2 + 3 + 4 + 5).

Lastly, we replace the index.js with the following contents.

```javascript
import("./jsapi").then(module => {
    let m = module.new_js_map();
    m.set("Hi", "Hi");

    console.log(m); // prints Map { "Hi" ->  "Hi" }

    console.log(module.set_get_js_map());  // prints "bar"

    console.log(module.run_through_map()); // prints 15
});
```

To run the above code, first compile the Rust into WebAssembly module by using:

```bash
cargo build --target="wasm32-unknown-unknown"
```

Then run 

```
wasm-bindgen target/wasm32-unknown-unknown/debug/jsapi.wasm --out-dir .
``` 
to generate the JavaScript bindings for the WebAssembly module.

Finally install the dependencies using `npm install` and run `npm run serve`. Now spin up the browser and open the developer console to see the expected results.

---

### What happens here?

Let us start with the generated JavaScript binding file. The generated binding files have almost the same structure as above, but with a few more functions exported.

The heap object is used as a stack here. All the JavaScript objects that are shared or referenced with the WebAssembly modules are stored in this heap. It is also important to note that once the value is accessed it is popped out from the heap.

The `takeObject` function is used to fetch the object from the heap. It first gets the object at the given index. Then it removes the object from that heap index (i.e., pops it out). Finally, it returns the value.

```javascript
function takeObject(idx) {
    const ret = getObject(idx);
    dropObject(idx);
    return ret;
}
```
---

{% github sendilkumarn/jsapi %}


---

---

If you have enjoyed the post, then you might like my book on Rust and WebAssembly. Check them out [here](https://sendilkumarn.com/wasm-book)

---

Do you know RustWASM enables you to use webAPIs too, check out

{% link https://dev.to/sendilkumarn/create-dev-s-offline-page-with-rust-and-webassembly-21gn %}

---

Similarly, we can use JavaScript APIs inside the Rust. The bindings are only generated for the common JavaScript API (including Node.js and the browser). Check out here for all the supported API [here](https://docs.rs/js-sys/0.3.15/js_sys/#structs).

Check out more about JavaScript APIs [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)

Check out more about from and into [here](https://doc.rust-lang.org/rust-by-example/conversion/from_into.html)

---


You can follow me on [Twitter](https://twitter.com/sendilkumarn).

If you like this article, please leave a like or a comment. ❤️

--- 


Picture Courtesy: JS Gif - https://www.jstips.co/en/about/