Join us at HeatSync Labs for a tour of web assembly from Rust. Last months web talks left more questions than answers so we dig deeper into Wasm.

Come help us knock out some topics like:

  • Exporting Rust crates to NPM
  • Serverless with Cloudflare and Amazon
  • A continuation of our frontend web talk
  • The future of wasm outside of the browsers as scripting languages, on desktops, and even microcontrollers

Tour of Wasm ecosystem via @jacobrosenthal

  • tight event loop, games, fluid interfaces
  • math transformations (not offerd native by the os or browser, like ml commonly is now) audio, video. Basically before you had to hope your browser vendor shipped an api for mathy stuff, now you can build your own and ship it yourself.
  • sandboxed, safe to deal with user input or extend your existing environment for game scripting, plugins, etc

Most basic example

first thing make sure rust is installed

Well need a one more tool to start, the 'target' were actually cross compiling here rustup target add wasm32-unknown-unknown Then crate a new library we can link with cargo new --lib add1 cd add1

Next, we need to specify a special kind of library by adding this to this to the Cargo.toml

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

Then add this to our lib.rs a simple function that we can call from javascript

#[no_mangle]
pub extern "C" fn add_one(x: i32) -> i32 {
	x+1
}

cargo build --release --target=wasm32-unknown-unknown and now weve got a wasm file at target/wasm32-unknown-unknown/release/add1.wasm

Lets make some terrible index.html

<!DOCTYPE html>
<html>
	<head>
		<script>
		fetch('./target/wasm32-unknown-unknown/release/add1.wasm')
		.then(response => response.arrayBuffer())
		.then(bytes => WebAssembly.instantiate(bytes)).then(results => {
			instance = results.instance;
			document.getElementById("demo").innerText = instance.exports.add_one(41);
		}).catch(console.error);
		</script>
	</head>
	<body>
		<h1>Wasm Demo</h1>
		<p>1 + 41 equals</p>
		<p id="demo"></p>
	</body>
</html>

and run a server to view it python -m SimpleHTTPServer 8000

Digging deeper for the nerds

so lets get complicated for a minute, feel free to ignore this,just to get your familiar

some tools IM going to use you dont need these I just want to show you under the hood

  • https://github.com/WebAssembly/wabt brew install wabt
  • https://github.com/WebAssembly/binaryen brew install binaryen

The wasm language actually instantiated in a .wat file, and you can write it yourself, though you really shouldnt. The idea is you could write in any language, and then 'target' wasm(wat) which would then be compiled to .wasm and shipped around for delivery, the same way that typescript wrote some higher level language (js plus types) and the compiled down to regular javascript

Lets try touch add1.wat and all all this to it

(module
  (func $add_one (param $lhs i32) (result i32)
    local.get $lhs
    i32.const 1
    i32.add)
  (export "add_one" (func $add_one))
)

wat2wasm add1.wat -o add1.wasm

We can look inside our wasm file to see a ton of stuff including what functions it exports wasm-objdump -x target/wasm32-unknown-unknown/release/add1.wasm

...
Custom:
 - name: "name"
 - func[0] <__wasm_call_ctors>
 - func[1] <add_one>
...

Now edit our html file and pull this wasm file instead of our rust generated one and Run our server again python -m SimpleHTTPServer 8000

So we can see how we can hand craft wasm, or we can more commonly use a language like rust to target it.

Rust to npm workflow

We could start hooking more stuff up to our bare skeleton from before, but theres a template tool called wasm-pack thatll get us further faster

  • cargo install wasm-pack
  • npm init rust-webpack my-app
  • cd my-app

Note this is just your normal npm and webpack skelton, but with a crate stuck in the mix. Take a look at src/lib.rs theres a bit more boilerplate and we have a package.json and a build and start alias which will call wasm-pack for you with, so we can just call the familiar npm run start and when the browser opens well find our hello world in the console.

Note its bringing in a new project web-sys autogenerated bindings to all the web api. Almost everything gated by features, need to turn stuff on to get small code size

Theres also js-sys autogenerated JavaScript APIs that are guaranteed to exist in all standards-compliant ECMAScript environments, such as Array, Date, and eval. This is only if you need to generate one of these structures to pass back to a javascript function of some kind

Maybe youd like to import a function from your existing js code?

There are more examples which dig way deeper at the wasm-bindgen book

Digressions

debugging

But can you debug this? Yep

native

Just like node packaged the v8 runtime so you can run your js code on your laptop and server, wasm is doing the same with the wasi standard. wasi is wasm but with libc syscalls mapped to your native environment, so it can get access to do things like read input and output, spawn threads, networking etc. So you can see a future where instead of say snap packages or installers with well ship wasm applications with excellent cross platform support.

Lets try the wasmtime interpreter.

  • cargo install wasmtime
  • rustup target add wasm32-wasi
  • cargo new helloworld
  • cd helloworld

notice this is a binary not a library, and notice no annotations or anything like before!

Lets add something that uses a syscall to main.rs to read text from the command line and write it back out

use std::io::{self, BufRead};

fn main() {
    let stdin = io::stdin();
    let mut iterator = stdin.lock().lines();

    println!("type your name");

    let name = iterator.next().unwrap().unwrap();

    println!("hi {}!", name);
}

cargo build --target wasm32-wasi And run it with the local cli interpreter wasmtime target/wasm32-wasi/debug/helloworld.wasm

Now you could ship this same .wasm to another laptop that has wasmtime installed but is a different architecture or os and it will run there too. It could run on another desktop, mac, linux, windows. Eventually it will work on raspberry pi and arm devices and mobile, but much of that isnt ready yet sadly.

Eventually these wasi things will run on the web without javascript annotations we did before. In fact theres a js polyfill but its just proof of concept and doesnt support std::io or anything interesting atm. Keep an eye on it.

Wasm as Container Technology

BUT WAIT THERS MORE The security capabilites opt in and sandbox means its far more modern take than other interpreted byte code languages. This makes it more like Docker than node.

"If WASM+WASI existed in 2008, we wouldn't have needed to created Docker. That's how important it is. Webassembly on the server is the future of computing. A standardized system interface was the missing link. Let's hope WASI is up to the task!" - Cofounder of Docker

To that end, theres starting to be research on running in along side your os instead of on top, ring0 in projects like wasm-kernel

Wasm for constrained devices

BUT WAIT Why stop at computers with tons of ram. micropython or jerryscript are running byte code of high level languages on constrained devices. Yes. wasm is the future here too. Sadly its a C project not Rust, but theres an interpreter for constrained devices like microcontrollers from Intel

Wasm as plugin scripting

BUT WAIT THERES MORE Scripting languages in games, photoshop plugins, etc already do a lot of scripting. Youve probably been excited to find out some program or game allows you to script it but then found out its only in Python or Lua. This space is for wasm too. That way you can have scripting in your app but support whaever langauge the likes, and get better sandboxing and security for free.

BUT WAIT It will be embedded in even more lower level stuff. postgres is talking about using wasm to ship some peice of code you want to run your results against right on the server.

Its designed to be THE common target, thats especially good at user input because of the sandboxing

Conclusion

Think of wasm like any other interpreted language, ruby, javascript

We could ship the ascii text human readable script, like a .js across the web and it will run anywhere because you already previously downloaded a native interpreter for your systems (Chrome or node or ruby in this case). The interpreter will tokenize the ascii line by line and compile it and run it

And modern interpreters like v8 for javascript will also in the backgroup compile it to byte code, so it doesnt have to recompile it, but that takes time so they just start the interpreter and swap them out when its ready and you never notice the different..

Thats wasm, except lets have a singular language all the other languages can target so you can bring your favorite programming langauge with you and embed the result from websites to native to serverless to games and plugins to microcontrollers.

Resources:

  • https://wasmbyexample.dev
  • https://rustwasm.github.io/book/
  • Programming WebAssembly with Rust https://pragprog.com/book/khrust/programming-webassembly-with-rust
  • Main book that links to the wasm bindgen and wasm pack books https://rustwasm.github.io/docs/book/introduction.html

Wasm for serverless via @monteslu

Walkthrough and demo of the Wrangler tool from Cloudflare

  • https://blog.cloudflare.com/introducing-wrangler-cli/
  • https://developers.cloudflare.com/workers/templates/boilerplates/rustwasm/

Crates you should know via @bitmage