Recently there has been quite a bit of talk about WebAssembly, a new format for code for the web. It is a compile target for languages like C and Rust that enables us to write, and run, code from these languages in our browser.
Disclaimer: WebAssembly is stabilized, but most implementations are not. The information contained here may become out of date or be incorrect, despite working at the time of writing.
Before we start please make sure you're using the current (or developer) version of Firefox or Chrome. You can check out
chrome://flags/ and make sure
wasm related things are enabled.
What Are We Looking At?
WebAssembly can be thought of as similar to asm.js. Indeed, we can use Emscripten compiler to target both.
Most existing documentation discusses how to build C, C++, or Rust into wasm, but there is nothing excluding languages like Ruby or Python from working as well.
Installing The Tools
We'll need two things to get started with WebAssembly and Rust, assuming you already have a functional development environment otherwise (That is, you have
build-essential, XCode, or the like installed.)
First, Rust. You can review here for a more long winded explanation of how to do this, or you can just run the following and accept the defaults:
Open a new terminal or run
source $HOME/.cargo/env, then we'll add the
wasm32-unknown-emscripten compile target via
rustup as well:
We can use this command to install other targets, found via
rustup target list, as well.
Next we need to set up Emscripten via
emsdk. We'll use the incoming version of Emscripten in order to get the best output. Note: This may take awhile (an hour!).
At this point Emscripten is installed. The last command will instruct us how to add the binaries to our path for permanent usage, or we can just
source ./emsdk_env.sh for temporary fun.
At this point
emcc -v should report something similar to this:
()) ) ) ) )
Now, let's kick the tires a bit!
First Experiment: Standalone Executable
In our first experiment we'll compile some Rust code into wasm and have it run in the browser console. We'll try a basic code sample to ensure things are working as we expect. We won't try to do any crate importing, DOM manipulation, or network access yet, but that's coming later!
Let's create our project, we'll use this same project for our future experiments as well. We need to set this to nightly (at least at the time or writing) to have our demos work.
Then we'll put our first code sample into
Running this normally yields what we would expect:
Now, let's get the same thing in our browser! We can build our executable for the wasm target by using the
cargo created several files in
target/wasm32-unknown-emscripten/release/deps/ for us. Of primary interest are the
Why do we get both a
wasm and a
At this point we can't really use the created files, since we don't have a webpage to import them into! Let's work on that. We can create a
site/index.html with the following content:
Next we need to set up some way to get the generated files from the
target/ folder into the
make is a good solution for this, so let's make a
Makefile. (Makefiles require tabs, not spaces!)
SHELL := :
Finally, test it with
Let's test our generated code by running
python -m SimpleHTTPServer, browsing to
http://localhost:8000/site/, and opening the browser console.
In the browser console you should see:
trying binaryen method: native-wasm asynchronously preparing wasm binaryen method succeeded. The character encoding of the HTML document was not declared. The document will render with garbled text in some browser configurations if the document contains characters from outside the US-ASCII range. The character encoding of the page must be declared in the document or in the transfer protocol. South false
Excellent! It worked!
Second Experiment: Imports
In our first example we just used a basic enum and some print statements, nothing too exciting. Let's do something more exciting and work with iterators.
use HashMap; use *;
Building this code again with
make we can navigate to the page again in our browser. This should output:
Excellent! So importing things from
std seems to work okay. How about other crates? Let's try using
itertools. Add the following to the
 = "*"
Then we can go and try to use it.
extern crate itertools; use Itertools; use *;
Building and visiting the page again we see the following output:
[North, South, East, West]
Okay that was simple! What about something a bit more complicated, like
serde, which lets us serialize and deserialize various formats? This will be important for an application which needs to parse responses from APIs!
Cargo.toml and the
 = "*" = "*" = "*"
extern crate serde_json; extern crate serde_derive;
Building, and viewing it in the browser console yields:
WebAssembly only supports a limited number of value types:
i32: 32-bit integer
i64: 64-bit integer
f32: 32-bit floating point
f64: 64-bit floating point
Notice how this doesn't include strings or other more satisfying types. That's ok! We can still use a function called
Module.cwrap to define the parameters and expected return values of the wasm exported functions.
Writing this interaction code means that on the Rust side of things we have to treat it as though we're interacting with C. This can make some things a bit complicated, on the bright side it means writing more FFI (foreign function interfaces) later will be much easier.
use c_char; use CString; use HashMap;
First thing you might notice is that we have a
index.html to write a bit of new code for this purpose.
The final argument of
cwrap is an array of argument types, which we don't use in this case.
It's also possible to call the function via
Module._get_data() but you'll notice that it returns a pointer to a memory location, not a string.
There isn't a tremendous amount of writing about this topic, the best resource I was able to find was in the 'Interacting with code' section of the Emscripten documentation.
The basic concept is that when
emcc goes and does its job it includes a
library.js file which we can add functions to via the
--js-library flag. These functions can return values, or do things like run
In order to do this we need to create a
site/utilities.js file containing the following:
'use strict'; ; LibraryManager.library, library;
Then the HTML:
Lastly, the Rust code:
extern use c_char; use CStr; extern
If we build this and load the page we'll get an alert, and see
"Hello from JS" printed out on the console.
The good thing is it works. The bad thing is that it is rather complex to do. Hopefully it will get better in the future.
I'm not entirely happy with this situation, if you have any ideas how we can do this better please let me know!
Fifth Experiment: Using The Web Platform
Luckly for us, there is a crate called
To get started, let's add
webplatform = "*" to our dependencies. Then we can use some slightly modified sample code from the repo:
extern crate webplatform;
Building our project you should see:
- DOM elements created when the wasm is loaded.
- DOM elements being created when your mouse goes over the button.
- An alert when you click on the button.
Due to the youth of the library there isn't much in the way of examples or documentation, so be prepared to fumble around in the dark. For example, I discovered the key to getting some events and
println!() working was to use
If you're looking for something to contribute, this crate would likely be an excellent place to do so! There's a bunch of work to be done and you could really make a difference for the blossoming Rust wasm community!
The future of Rust on the web is quite bright! Much of the foundations already exist, as we've seen in this article, and the Rust community is actively interested in improving the experience. The Rust repository even has an
A-wasm tag for issues!
There are currently discussions about moving from
emcc to the nascent LLVM wasm backend which you can track here. This may make significant parts of this article obselete, which is very exciting!
The ecosystem around wasm in Rust is still young, and now is a great time to get involved and help shape the future!
Special thanks to Jan-Erik (Badboy) for his workshop at Rust Belt Rust 2016, his hard work on this ecosystem, his advice while writing this post, for reviewing this before publishing, and most of all for being my valued friend. May we continue to hack in the same circles.
This post was supported by Asquera and is an extension of the wasm chapter in our Three Days of Rust course. If you'd like to hire us for training, consulting, or developing in Rust (or anything else) please get in touch with me at email@example.com.