Skip to content

jkomyno/eurorust-2023

Repository files navigation

Underrated gems of Rust & WebAssembly: Errors · Async · I/O


Slides for this talk are also available here.

Abstract

WebAssembly has revolutionised cross-platform development, enabling developers to compile their code just once, regardless of the runtime operating system or CPU architecture. While Rust boasts the best language support for WebAssembly through the wasm-bindgen tool, developers often encounter unexpected challenges and differences in execution models. Similarly to no_std, not every Rust code can be compiled to Wasm out of the box.

For example, in a Rust library compiled to Wasm, panics cannot be unwinded (and their stacktrace location is lost), and async functions à la tokio cannot be awaited. Moreover, no I/O operation can be performed in WebAssembly: that means no network connections nor file handles can be opened. The WASI specification partially overcomes this limitation, but that’s still unstable and doesn’t cover network sockets.

What if you wanted to write a production async Rust library that needs both robust error handling and I/O, and you needed to run it in JavaScript runtimes like Node.js?

In this talk, we will explore uncharted territories in Wasm land, where you’ll learn how to confidently integrate your familiar Rust patterns with powerful - but fragile - WebAssembly runtimes. As a motivating example, we will present a database query engine prototype written in Rust+Wasm that accepts a JSON query, parses it into an equivalent SQL query, runs such query asynchronously and returns the results to the caller.

Just like pirates in a sea of compilation errors, we will bend the rules to conquer new horizons in the realm of truly cross-platform Rust libraries!

Requirements

(*) These are the versions used to develop this repository. Older versions might work as well, but they haven't been tested.

Furthermore, you'll need to install:

  • wasm-bindgen, via:
    cargo install -f wasm-bindgen-cli@0.2.87

You'll also need to enable the wasm32-unknown-unknown compilation target for Rust, via:

rustup target add wasm32-unknown-unknown

Setup

  • Compile the Rust codebase and generate bindings for WebAssembly via:

    ./build.sh

    The generated WebAssembly bindings are in ./src/wasm, and are compatible both with Node.js / ESM, as well as Cloudflare Workers.

  • Run the TypeScript tests via:

    pnpm test

Query Engine Demo

The local query-engine crate demonstrates how to run simple INSERT / SELECT queries against an arbitrary database via WebAssembly.

See live demo.

How to setup

Locally:

  • To initialise the database from this SQL schema, run:
    npx wrangler d1 execute eurorust-2023 \
      --local --file=d1/schema.sql \
      --persistTo=.wrangler/state

On Cloudflare Workers

  • Authorize your CLI to interact with Cloudflare via:

    npx wrangler login
  • To initialise the database from this SQL schema, run:

    npx wrangler d1 execute eurorust-2023 \
      --file=d1/schema.sql

How to run

Locally:

On Cloudflare Workers:

Demos

demo-errors

The local demo-errors crate demonstrates how to export Rust functions that may fail with a runtime error to TypeScript. It also contains the simplest case of panic: one without any panic hook.

use serde::{Deserialize, Serialize};
use wasm_bindgen::{prelude::wasm_bindgen, JsError};

#[derive(Serialize, Deserialize, ...)]
pub struct Event {
    name: String,
    year: u16,
}

#[wasm_bindgen(js_name = "parseWithError")]
pub fn parse_with_error(event: &str) -> Result<Event, JsError> {
    serde_json::from_str(event).map_err(|e| JsError::from(e))
}

The correspondent TypeScript tests are in ./__tests__/errors.test.ts.

demo-panic

The local demo-panic crate demonstrates how to export Rust functions that may trigger a panic. It also contains a custom panic hook initialiser in order to let the JavaScript runtime understand in which Rust file the panic was originated.

The correspondent TypeScript tests are in ./__tests__/panic.test.ts.

demo-async

The local demo-async crate demonstrates how to use tokio utilities such as tokio::sync::RwLock with WebAssembly.

The correspondent TypeScript tests are in ./__tests__/async.test.ts.

demo-io

The local demo-io crate demonstrates how to programmatically trigger I/O tasks from Rust / WebAssembly, via JavaScript functions.

The correspondent TypeScript tests are in ./__tests__/io.test.ts.

👤 Author

Alberto Schiabel

Please consider supporting my work by following me on Twitter and starring my projects on GitHub. I mostly post about TypeScript, Rust, and WebAssembly. Thanks!

📝 License

Built with ❤️ by Alberto Schiabel. This project is MIT licensed.

About

Accompanying code for my talk "Underrated Gems of Rust & WebAssembly: Errors, Async, I/O" presented @ EuroRust2023 in Brussel

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published