Hey there, you slithery Rustilian.
Have you heard the good word? Rust can now target asm.js and WebAssembly (wasm) via
emscripten. That means you can run Rust code on the web, and this capability
is available on Rust nightlies right now! Let me tell you how to Rust the web.
First, a disclaimer. This support is still a work in progress and there are
going to be a whole lot of bugs (report them, you better). It's at the stage
where it passes the test suite, but has not been vetted on real projects at
all. And the runtime properties of the web are far different from traditional
platforms - in particular most of Rust's I/O stack is unimplemented and will
simply fail at runtime, and badly. No work has gone into optimizing these
ports, and in particular unwinding support is known to be excruciatingly slow
in emscripten. You're on the bleeding edge. You are a pioneer.
wasm support is almost entirely untested, but is likely to work. The wasm format
is not finalized. The way rustc emits wasm by default is by emitting a
javascript file that contains a wasm interpreter, alongside a wasm file. If
the interpreter detects that the JS engine supports wasm (which it almost
certainly won't!) then it will use the native implementation, if not it will use
its own interpreter. So it's gunna be slow.
The setup
We need rustup and the emscripten SDK. We'll install the Rust nightly
toolchain and the emscripten incoming toolchain. I've only tested this on Linux,
but macOS should work similarly. The Windows steps will be slightly different,
but in theory it should be possible there as well.
Rust installation:
curl -L https://sh.rustup.rs | sh -s -- -y --default-toolchain=nightly
source ~/.cargo/env
rustup target add asmjs-unknown-emscripten
rustup target add wasm32-unknown-emscripten
If you already have rustup installed, then instead of running the curl
and
source
commands above, just switch to the nightly toolchain with rustup default nightly
.
Emscripten installation:
curl -O https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz
tar -xzf emsdk-portable.tar.gz
source emsdk_portable/emsdk_env.sh
emsdk update
emsdk install sdk-incoming-64bit
emsdk activate sdk-incoming-64bit
Running some code
Hello world in asm.js:
echo 'fn main() { println!("Hello, Emscripten!"); }' > hello.rs
rustc --target=asmjs-unknown-emscripten hello.rs
node hello.js
Hello world in wasm:
echo 'fn main() { println!("Hello, Emscripten!"); }' > hello.rs
rustc --target=wasm32-unknown-emscripten hello.rs
node hello.js
Note that when I ran these commands the first time the build took a long
time. It looks like emscripten is doing a lazy compile of binaryen the first
time you run it. Just give it time.
Building a real project
That's just the simplest thing, but cargo works too. For some examples of all this
in action see @jer's demo page.