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:
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/[email protected]/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/[email protected]/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