Introducing WebAssembly using Rust

Pankaj Baagwan
ducktyp’d
Published in
6 min readJan 13, 2023

What is WebAssembly (WASM)?

WebAssembly (abbreviated WASM) is a binary instruction format for stack-based virtual machines. WASM is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

This article originally published here on January 7, 2023

Why use WebAssembly (WASM)?

WASM allows developers to build high-speed web apps in the language of their choice. However, it would be a wrong notation that WebAssembly is only for web applications. It can be used for other non-web-based applications as well

WebAssembly is not going to replace JavaScript or any other language for that matter. It is just a binary format that promises near-native performance on any target. It can be a compilation target for any programming language.

In this article we will cover how to compile Rust code to WebAssembly. We can write some Rust code, call JavaScript functions from it or expose functions to JavaScript for further use.

Use cases for WebAssembly

There are two major use cases for Rust and WebAssembly:

  • Entire Application: Build an entire web application using Rust and compile it to WebAssembly
  • Partial application: Build partial application using Rust and compile it to WebAssembly. Then use it with an existing JavaScript frontend.

In this article, we are going to cover the latter use case for illustration. If you are interested to build a complete web application using rust, check out yew project.

To build a wasm app we will be using package wasm-pack to build a package. wasm-pack is a tool for building JavaScript packages in Rust. This implementation will contain WebAssembly and JavaScript code, so to run that package users will not need Rust installed. Since final product would be WebAssembly and JavaScript, users will not even know its built using Rust.

Environment Setup

To build the package we would need an environment setup. Let’s go through all software and dependencies needed.

Rust

First, we would definitely need Rust installed. If you do not have it setup already, please head to Rust Installation page and install by following the instructions. This installs a tool called rustup, which lets you manage multiple versions of Rust. By default, it installs the latest stable Rust release, which you can use for general Rust development.

rustup installs rustc, the Rust compiler. It also installs cargo, package manager for Rust, rust-std, standard libraries of Rust’s, and documentations rust-docs.

Note: Pay attention to the post-install note about needing cargo’s bin directory in your system PATH. This is added automatically, but you must restart your terminal for it to take effect.

wasm-pack

Now to trans-compile our code into WebAssembly; we need an additional tool, a wasm-pack package. Once installed and added as dependency to our project, this will help compile the code to WebAssembly, as well as produce code structure to be used in browser.

To download and install it, enter the following command into your terminal:

cargo install wasm-pack

Building our WebAssembly package

Now let us create a package in Rust. Open a command prompt, navigate to a folder where you would like to create this package and then run following command in command prompt

cargo new --lib first-wasm
//=> Created library `first-wasm` project

If you list the content of the present working directory, you should notice a folder named first-wasm. This is where a rust package has been created. Now go into first-wasm the folder and list its contents. If you are on UNIX based OS, you may use tree commands, like following

tree
//+-- Cargo.toml
//+-- src
// +-- lib.rs

Let’s dissect what we are looking at. Cargo.toml is a package file, that contains or relays important information about the package (i.e config, structure, dependencies, and how to build). If you are already familiar with Gemfile in Bundler in Ruby or package.json in NodeJS’s npm. It is similar to that.

Next we are going to look at src/lib.rs that cargo has generated for us.

#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

It is just an assertion test or a placeholder. Hence we are not going to use this. You can go ahead and remove this code.

Let’s write our WASM code in Rust

Let us write some Rust code that would transpile to WASM. Put following content into src/lib.rs. Make sure that you removed existing code.

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}

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

Let’s dissect the code above. It has three major parts that we will discuss here. We will not be going into Rust dictionary and syntax, for that please read Rust By Example Book

Using wasm-bindgen to communicate between Rust and JavaScript

The first line in our Rust code is the following:

use wasm_bindgen::prelude::*;

Here the use keyword, imports code from library into your code. In Rust, libraries are called crates. In this case, use is importing everything from the wasm_bindgen::prelude module. We use these features in the next section. wasm-bindgen is coming from a wasm-pack package to create a bridge between Rust code and JavaScript. It allows JavaScript to call Rust API and/or Rust function with the required parameters. In short, it generates bindings between JavaScript and Rust

We use wasm-bindgen’s functionality in our package. In fact, that’s the next section.

Calling external JavaScript functions from Rust

In our code, the next four lines looks like following:

#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}

The call inside the #[] is called an attribute, and it modifies the next statement. In our case, the statement is question is extern. This tells Rust that we want to call some externally defined functions. The attribute points to where to find those functions, in our case it is wasm-bindgen and it handles the binding.

The next line defines a public function in Rust, named alert, which takes a string as an argument and assigns it to s

As you already may know, alert the function is available in JavaScript and we are calling it from Rust using wasm-bindgen

Whenever you want to call any JavaScript functions, you just add them to this file, and wasm-bindgen will take care of setting everything up for you.

Beware that, not everything is supported yet, but we’re working on it. At the time of writing this article, this functionality is still Work-in-Progress.

Producing Rust functions that JavaScript can call

Now look at the remaining lines of code, below:

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

Once again, we see the #[wasm_bindgen] attribute. In this case, it’s not modifying an extern block, but a public function fn; this means that we want this Rust function to be able to be called by JavaScript.

This is the opposite of extern; these are the functions that we are exposing to the JavaScript front-end from Rust via WebAssembly.

This function is named greet, and takes one argument name, a string. It then calls the alert function we asked for in the extern block above. It passes a call to the format! macro, which lets us interpolate strings.

The format! the macro takes two arguments in this case, a format string, and a variable to put in it. The format string is the "Hello, {}!" bit. It contains {}, where variables will be interpolated. The variable we’re passing is name, the argument to the function, so if we call greet("Pankaj") we should see "Hello, Pankaj!".

This is passed to alert(), so when we call this function we will see an alert box with "Hello, Pankaj!" in it. Now that our library is written, let’s build it.

To read more click here

--

--

Pankaj Baagwan
ducktyp’d

Architect, Tech Innovator, Certified Ethical Hacker and Cyber Security Enthusiast