Rust & WebAssembly (WASM)
WebAssembly is where Rust meets the browser: you compile a Rust crate to a compact .wasm binary that runs alongside your JavaScript at near-native speed, with no garbage collector to ship and full type-checking across the boundary. This section maps the front-end toolbox you already know (tsc + a bundler, lib.dom.d.ts, fetch/localStorage/setTimeout, React/Svelte components) onto their idiomatic Rust counterparts. You will set up wasm-pack and wasm-bindgen, call JavaScript from Rust and Rust from JavaScript, reach Web APIs and the DOM through web-sys, build whole UIs with Yew and Leptos, and tune bundle size, the JS↔WASM boundary cost, and deployment.
The repository’s pinned verification toolchain uses the 2024 edition; cargo new selects that edition automatically. The recorded dependency set is wasm-bindgen 0.2.122, web-sys/js-sys 0.3.99, wasm-bindgen-futures 0.4.72, serde-wasm-bindgen 0.6, and wasm-pack 0.13; complete projects are checked against wasm32-unknown-unknown as described by the verification policy.
What You’ll Learn
Section titled “What You’ll Learn”- What WebAssembly is, what runs where, and when Rust→WASM genuinely beats plain JavaScript (and when it does not)
- How to set up a
cdylibcrate and drivewasm-packto emit a browser-ready, npm-installable package - How to export a Rust function with
#[wasm_bindgen], build it, and call it from a web page - How to call JavaScript from Rust with
#[wasm_bindgen(module = ...)]imports and thejs-sysbuilt-ins - How exported functions and structs appear from JavaScript, and what the generated JS/
.d.tsglue actually does - Which types can cross the boundary, what
JsValueis, when to reach forserde-wasm-bindgen, and how to hand a closure to JavaScript without leaking or crashing - How to use Web APIs (
fetch, timers,localStorage) from Rust throughweb-sysand its feature-flag system - How to query, create, and wire up DOM elements and event listeners from Rust
- How the Yew (component/Elm-like) and Leptos (fine-grained reactivity) frameworks let you write a whole UI in Rust
- How to measure and shrink bundle size (
wasm-opt,twiggy), reason about the boundary cost, and decide when WASM wins - How to deploy a WASM app: bundlers (Vite/webpack), serving
.wasmwith the right MIME type, and CDN caching
Topics
Section titled “Topics”| Topic | Description |
|---|---|
| What Is WebAssembly? | What WASM is and why use Rust for it; realistic use cases vs plain JavaScript, and what runs where. |
| Setting Up wasm-pack | The build toolchain: project structure, the cdylib crate type, and the web/bundler/nodejs build targets. |
| Your First Rust → WASM Module | A #[wasm_bindgen] export, one build command, and calling the result from a web page. |
| Calling JavaScript from Rust | Importing JS into Rust via #[wasm_bindgen(module = ...)] and the js-sys standard-library bindings. |
| Calling Rust from JavaScript | Exporting functions and structs, and what the generated JS glue and .d.ts look like to the consumer. |
| The wasm-bindgen Deep Dive | Types crossing the boundary, JsValue, serde-wasm-bindgen, and closures/callbacks. |
| Using Web APIs from Rust | fetch, timers, and localStorage from Rust with web-sys, and the Cargo feature-flag system. |
| DOM Manipulation from Rust | Reading the document, creating elements, and attaching event listeners with web-sys. |
| Frontend Frameworks: Yew & Leptos | Yew (component/Elm-like) and Leptos (fine-grained reactivity): overview plus a tiny example of each. |
| WASM Performance | Bundle size (wasm-opt, twiggy), the JS↔WASM boundary cost, and when WASM actually wins. |
| Deploying WASM Apps | Bundlers (Vite/webpack), serving .wasm with the correct MIME type, and CDN caching. |
Learning Objectives
Section titled “Learning Objectives”By the end of this section, you will be able to:
- Decide whether a given workload belongs in WebAssembly or stays in JavaScript, and justify the choice
- Scaffold a
cdylibcrate and produce apkg/directory withwasm-packfor the right target - Export Rust functions and structs to JavaScript and consume them, including the
await init()step and explicitfree() - Import JavaScript functions, globals, and npm packages into Rust, and bridge a JS
PromisewithJsFuture - Reason about which Rust types cross the boundary cheaply, which copy, and which need
serde-wasm-bindgen - Hand a Rust closure to JavaScript and manage its lifetime so it neither dangles nor leaks
- Drive Web APIs and the DOM from Rust with
web-sys, enabling only the feature flags you need - Sketch a UI in Yew or Leptos and explain the difference between a virtual-DOM and a fine-grained-reactive model
- Profile and shrink a
.wasmbinary, and design a coarse-grained boundary that crosses rarely with large payloads - Wire the build into a bundler and serve and cache the
.wasmartifact correctly in production
Prerequisites
Section titled “Prerequisites”- Section 12: Modules and Packages — a WASM project is a library crate with a
[lib]crate-type, and you will lean on crates,Cargo.toml, and feature flags throughout. - Section 15: Serialization:
serde-wasm-bindgenreuses the Serde derive model to move structured data across the JS↔WASM boundary.
A working knowledge of the earlier fundamentals — ownership (why exported structs need free()), error handling (Result becomes a thrown JS exception), and async (Rust futures are lazy, unlike eager JS Promises) — will also help.
Estimated Time
Section titled “Estimated Time”Approximately 14 hours, including reading, hands-on practice, and the per-topic exercises.
Continue to Section 20: Unsafe & FFI to apply the same “talk to another world” skills to native C code: the extern "C" and cdylib machinery that wasm-bindgen builds on.
Frequently asked questions
Section titled “Frequently asked questions”How do I run Rust in the browser?
Section titled “How do I run Rust in the browser?”Compile a crate to WebAssembly with wasm-pack, then import the generated module from JavaScript. wasm-bindgen produces the JS glue and the TypeScript types for your exported functions. See Your First WASM Module.
When is Rust and WASM actually faster than JavaScript?
Section titled “When is Rust and WASM actually faster than JavaScript?”For CPU-bound work such as parsing, image processing, or crypto, once the module is loaded. Every JS↔WASM boundary crossing has a cost, so the win comes from doing substantial work per call rather than many tiny calls. See WASM Performance.
Can Rust call browser APIs like fetch or the DOM?
Section titled “Can Rust call browser APIs like fetch or the DOM?”Yes, through web-sys, the typed bindings to browser APIs (the WASM equivalent of lib.dom.d.ts). You enable the features you need and call document, fetch, and friends from Rust. See Web APIs from Rust.