Deno logoDeno
Ferris the Crab hanging out with the Deno dinosaur on the beach.

wasmbuild - Using Rust in Deno and Web Apps


When writing JavaScript, it's sometimes useful to call into Rust code. For example, there might be a complex algorithm already implemented in Rust that you want to reuse, or some Rust code might run faster and be more memory efficient than any JS implementation.

This blog post introduces a new tool called wasmbuild, which simplifies the process of building and using Rust code in Deno and the browser.

Overview

wasmbuild is a CLI tool that generates glue code for calling into Rust crates using wasm-bindgen. The generated output can be easily consumed in JavaScript via WebAssembly. wasmbuild includes multiple loader strategies, lazy instantiation, and automatically optimizes the output with the wasm-opt tool.

This is very similar to tools like wasm-bindgen-cli and wasm-pack, but is a script you can run without you or your contributors having to install a global CLI tool beyond Rust's toolchain and Deno. Additionally, it is specifically geared for Deno and the browser.

Example - Adding two integers

For this example, we're going to start from scratch creating a function in Rust that adds two numbers, then use that function in our JavaScript.

Prerequisites

To begin:

  1. Install Deno.
  2. Install Rust via Rustup.

After installation, ensure that deno -V, rustup -V, and cargo -V all work.

Setting up Wasmbuild and Rust

Open a terminal, create a new folder for your project, and change your current working directory to be inside that project.

mkdir wasmbuild_example && cd wasmbuild_example

In that directory, create a deno.json file with a wasmbuild task defined for Deno's task runner as shown:

{
  "tasks": {
    "wasmbuild": "deno run -A https://deno.land/x/wasmbuild@0.8.5/main.ts"
  }
}

Now, run the task with the new argument:

deno task wasmbuild new

This will scaffold out a starter Rust crate in the rs_lib folder of your project, along with an example function:

// rs_lib/src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
  return a + b;
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn it_works() {
    let result = add(1, 2);
    assert_eq!(result, 3);
  }
}

This file defines a function called add, that takes two signed integers and returns a signed integer back. It's then exported for use in JavaScript by the #[wasm_bindgen] attribute.

For our example, we'll stick to using this generated code.

Building

To build the project, run the wasmbuild task:

deno task wasmbuild

This will create a lib/rs_lib.generated.js file and lib/rs_lib_bg.wasm file.

Using it in Deno

To use it in Deno, create a main.js file with the following code:

import { instantiate } from "./lib/rs_lib.generated.js";

const { add } = await instantiate();

console.log(add(1, 2));

This will instantiate the Wasm module, call its add function, and output the result to the console.

Try it out by running:

deno run main.js

The output should be 3.

Using it in the Browser

Create a very basic index.html file that references the main.js file we created previously:

<html>
  <script type="module" src="main.js"></script>
</html>

Now launch an http server that serves files in the current working directory. We can do this easily by running the following command:

$ deno run --allow-net --allow-read=. https://deno.land/std@0.144.0/http/file_server.ts
Listening on http://localhost:4507/

Navigate to the link shown in order to have the index.html file served, and open the browser console. You should see 3 logged to the console.

Using it in Deno Deploy

In addition to Deno's CLI and the browser, the generated code from wasmbuild will work out of the box on Deno Deploy.

Here's a playground that uses the example above (see it in action by clicking "Open Editor"):

This imports the above generated code from the https://github.com/denoland/wasmbuild_example repo, instantiates the Wasm module to get the add function, then starts a web server that responds to every request with the result of adding 1 and 2.

Generally with Deno Deploy, you would want to use Git integration to deploy this code. Note that a URL deployment won't work with the generated output of wasmbuild at the moment because the .wasm file is not part of the module graph. This will be fixed once Wasm modules can be imported, so if you're interested follow issue #2552 for updates.

More details

All of the code shown above is available at: https://github.com/denoland/wasmbuild_example

For more information about wasmbuild, see the repo at: https://github.com/denoland/wasmbuild