Josh Finnie PRO
Full stack staff engineer. Lives in the DMV.
Josh Finnie, Rustacean
Rust is a programming language designed for performance and safety, especially safe
concurrency.
Rust is syntactically
similar to C++, but can guarantee memory safety by using a borrow checker to validate references. [1]
[1] https://en.wikipedia.org/wiki/Rust_(programming_language)
fn main() {
// Special formatting can be specified after a `:`.
println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
// structs do not have access to everything by default,
// need to implement how Point is displayed to println
struct Point {
x: i32,
y: i32,
}
impl std::fmt::Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
let origin = Point { x: 1, y: 10 };
println!("The origin is: {}", origin);
}
$ cargo run helloWorld.rs
1 of 10 people know binary, the other half doesn't
The origin is: (1, 10)
WebAssembly (WASM) is an open web standard that defines a portable binary-code format for executable programs in the web giving near-native code execution speed in the web browser. [1]
[1] https://en.wikipedia.org/wiki/WebAssembly
You can read the specification here: https://webassembly.github.io/spec/core/intro/introduction.html
// helloWorld.wat
(module
(func (export "add") (param $n1 i32) (param $n2 i32) (result i32)
get_local $n1
get_local $n2
i32.add
)
)
// helloWorld.wasm
00 61 73 6d 01 00 00 00 01 07 01 60 02 7f 7f 01
7f 03 02 01 00 07 07 01 03 61 64 64 00 00 0a 09
01 07 00 20 00 20 01 6a 0b
// helloWorld.mjs
import * as M from './add.wasm';
console.log(M.add(10, 13)); // 23
$ node --experimental-modules --experimental-wasm-modules helloWorld.mjs
23
Note: This example is writing WebAssembly Text Format code from scratch and is a bit silly... I just wanted to show it to you for completeness. Read more here: https://dev.to/sendilkumarn/loading-wasm-as-esm-in-nodejs-2gdb
There are quite a few languages that can compile into WASM if Rust is not your cup of tea:
* 😬
Not only is WASM APIs existing in browsers, but there has been some work to get WASM running on servers. This is beyond the scope of this talk, but it's an interesting point to bring up.
It makes the general idea of adopting WASM into your workflow a bit more appetizing. For more information check out the work done on WASI.
https://wasi.dev/
use std::env;
use std::fs;
use std::io::{Read, Write};
fn process(input_fname: &str, output_fname: &str) -> Result<(), String> {
let mut input_file =
fs::File::open(input_fname).map_err(|err| format!("error opening input: {}", err))?;
let mut contents = Vec::new();
input_file
.read_to_end(&mut contents)
.map_err(|err| format!("read error: {}", err))?;
let mut output_file = fs::File::create(output_fname)
.map_err(|err| format!("error opening output '{}': {}", output_fname, err))?;
output_file
.write_all(&contents)
.map_err(|err| format!("write error: {}", err))
}
fn main() {
let args: Vec<String> = env::args().collect();
let program = args[0].clone();
if args.len() < 3 {
eprintln!("{} <input_file> <output_file>", program);
return;
}
if let Err(err) = process(&args[1], &args[2]) {
eprintln!("{}", err)
}
}
$ rustup target add wasm32-wasi
$ cargo build --target wasm32-wasi
$ wasmtime --dir=. --dir=/tmp demo.wasm test.txt /tmp/somewhere.txt
After seeing an example of writing Web Assembly from scratch, I want to introduce you to using Rust!
Surprise!
There are two main crates that combine to make your life easy. These two crates are:
wasm-bindgen is a crate that facilitates high-level interactions between WASM modules and Javascript. [1]
In other words, wasm-bindgen allows you to import Javascript things to Rust and export Rust things to Javascript without worrying too much about the complexity of doing so.
[1] https://github.com/rustwasm/wasm-bindgen
// module.rs
use wasm_bindgen::prelude::*;
// Import the `window.alert` function from the Web.
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
// Export a `greet` function from Rust to JavaScript, that alerts a
// hello message.
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
// module.js
import { greet } from "./hello_world";
greet("World!");
use std::f64;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
#[wasm_bindgen(start)]
pub fn start() {
let document = web_sys::window().unwrap().document().unwrap();
let canvas = document.get_element_by_id("canvas").unwrap();
let canvas: web_sys::HtmlCanvasElement = canvas
.dyn_into::<web_sys::HtmlCanvasElement>()
.map_err(|_| ())
.unwrap();
let context = canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>()
.unwrap();
context.begin_path();
// Draw a circle.
context
.arc(75.0, 75.0, 50.0, 0.0, f64::consts::PI * 2.0)
.unwrap();
context.stroke();
}
wasm-pack is a command-line application that seeks to be your one-stop-shop for building WASM packages generated by Rust. [1]
This tool simplifies the infrastructure required to get your Rust code compiled into WASM, gives you a nice Javascript wrapper class and allows you to publish to NPM all with a few commands, it's invaluable!
[1] https://github.com/rustwasm/wasm-pack
The wasm-pack build command is super powerful and really removes a lot of issues you'd have with other languages. Below are some flags that are indispensable.
This is the Rust/WASM Hello World example. You can find the tutorial for this here:
https://rustwasm.github.io/docs/book/game-of-life/introduction.html
This is a library that I wrote to understand the benefits of using WASM for command line applications in Node.js.
https://github.com/joshfinnie/wasm-frontmatter
Gray-Matter runtime: 3.859ms
WASM-Frontmatter runtime: 0.694ms
Gray-Matter output
{
content: '\nThis is an excerpt!\n\n---\n\n## Blog\n\nThis is a blog post!',
data: {
title: 'blog post',
categories: [ 'wasm', 'markdown', 'test' ]
},
excerpt: '\nThis is an excerpt!\n\n'
}
WASM-Frontmatter output
{
content: '\nThis is an excerpt!\n\n---\n\n## Blog\n\nThis is a blog post!',
data: {
title: 'blog post',
categories: [ 'wasm', 'markdown', 'test' ]
},
excerpt: '\nThis is an excerpt!\n\n'
}
Next steps for WASM-frontmatter:
Josh Finnie
@joshfinnie (almost everywhere)
By Josh Finnie