Getting Setup

Before we get started we need to make sure that a few more things are installed.

Install the Nightly Compiler

$ rustup install nightly

This will allow us to use some experimental rust features.

Install the wasm32-unknown-unknown Target

$ rustup target add wasm32-unknown-unknown --toolchain nightly

This will allow us to actually compile to Web Assembly, specifically for the nightly compiler we just installed

Install Wasm-Bindgen’s CLI tool

$ cargo install wasm-bindgen-cli

This will install a cli application wasm-bindgen, this is going to read our .wasm file and generate a javascript file for us.

Now that our environment can handle building a wasm project, let's get something working. We are going to use the same lib.rs file that we were using before but we need to update a few things about our project.

first we can delete the main.rs file, you don't have to but it might trip you up.

Once we have done that we need to update our Cargo.toml file with some new information. It is going to look like this.

Cargo.toml

[package]
name = "hello_world"
version = "0.1.0"
authors = ["robert masen <r@robertmasen.pizza>"]

[dependencies]
wasm-bindgen = "0.2"

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

First we added a dependency wasm-bindgen = "0.2", this will make a bunch of glue code available to us.

The other thing we changed was adding a [lib] section and including the line crate-type = ["cdylib"], this will tell cargo that we want to create a "C Dynamic Library" this will make sure our library is consumable from languages other than Rust. Now that we have our configuration right, lets dig back into the code. We really just need to make a few small changes to our existing lib.rs file.

lib.rs

#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn generate_greeting() -> String {
    "Hello, world!".to_string()
}
#[wasm_bindgen]
pub fn generate_custom_greeting(name: &str) -> String {
    format!("Hello, {}!", name);
}

First we added a new line to the top of the file (#![feature(...)]), this is turning on a few custom features that are not normally enabled in rust.

  • proc_macro: This enables procedural macros, a special kind of rust code we will be using
  • wasm_custom_section: This tells the compiler to add a non-standard section to our .wasm file
  • wasm_import_module: This instructs the compiler to include functionality from another wasm module

After that, we define our need for the external library extern crate wasm_bindgen; and then import everything in that library's prelude module with use wasm-bindgen::prelude::*;. This makes everything we need from that library available to us.

We also need to annotate the functions with the #[wasm_bindgen] procedural macro. If you are familiar with attributes in C# or decorators in TypeScript and Python, it is the same concept, if you are not this will just add a bunch of that glue code I was referring to earlier.

With those changes, we can now build our wasm module. We are going to use the new stuff we added with our rustup and cargo commands earlier.

$ cargo +nightly build --target wasm32-unknown-unknown

Notice that we put +nightly in there, this is shorthand for telling cargo to use the nightly compiler instead of the stable one

The first time you run this it will might take a little while.

Once that is complete you would have a .wasm file in the ./target/wasm32-unknown-unknown/debug/ folder.

The next step is to generate our bindings. We can use that wasm-bindgen command line tool we installed like so.

$ wasm-bindgen ./target/wasm32-unknown-unknown/debug/hello_world.wasm --out-dir .
This will generate 2 files in our project's root folder, hello_world_bg.wasm and hello_world.js. lets take a look at the .js file.