Rust
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
68: Compiling Rust to WebAssembly
Up until now, we've lived mostly in the terminal. But one of the coolest things about Rust is how well it plays with the web. By compiling Rust to WebAssembly (Wasm), we can run high-performance code in the browser alongside JavaScript. I'm not talking about replacing JS entirely—that's a fool's errand—but rather using Rust for the "heavy lifting" logic while JS handles the DOM.
Setting up the bridge with wasm-pack
To get this working, we need a tool that handles the compilation and generates the JS "glue" code. I always use wasm-pack. It's the gold standard here. If you haven't installed it yet, you'll want to run the installer from their official site. It handles the wasm32-unknown-unknown target for you, so you don't have to manually fiddle with the Rust toolchain targets.
For this lesson, we're going to build a simple XOR Cipher. It's a basic encryption tool: you give it a string and a secret key, and it returns an obfuscated string. It's a perfect little example because it involves byte manipulation—something Rust is incredibly efficient at.
Writing the encryption logic
First, I'll start with the code. We need the wasm-bindgen crate. This is what allows Rust types to communicate with JavaScript types. Without it, we'd be stuck passing raw pointers and memory addresses, which is a nightmare you don't want.
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn xor_cipher(input: &str, key: &str) -> String {
let key_bytes = key.as_bytes();
let input_bytes = input.as_bytes();
let result: Vec = input_bytes
.iter()
.enumerate()
.map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()])
.collect();
// We'll encode the result as a hex string so it's easy to display in HTML
result.iter()
.map(|b| format!("{:02x}", b))
.collect()
}
I used #[wasm_bindgen] on the function to tell the compiler, "Hey, make this available to JavaScript." I'm mapping over the bytes and using the modulo operator on the key length so the key repeats if the input is longer. Simple, clean, and fast.
The "Missing Link" mistake
Now, here is where I almost tripped up. I ran wasm-pack build and got a confusing error about the library not being a dynamic system library. I realized I'd forgotten to tell Cargo how to package the output.
By default, Rust builds a rlib. But for WebAssembly, we need a cdylib (C-compatible dynamic library). If you don't add this to your Cargo.toml, the build will fail or produce something the browser can't use. I had to go back and add this section:
[lib]
crate-type = ["cdylib"]
It's a small detail, but it's the number one reason people get stuck when they first try Wasm. Always double-check your crate-type.
Bringing the logic into the browser
Once I ran wasm-pack build --target web, it generated a pkg/ folder. Inside, there's a .wasm file and a .js file. The JS file is the "wrapper" that handles the memory allocation for us.
To use it in a real page, I just need a simple HTML file and a script tag. Note that I have to use type="module" because the generated JS is an ES module.
<script type="module">
import init, { xor_cipher } from './pkg/xor_cipher.js';
async function run() {
await init(); // Initialize the Wasm module
const secret = xor_cipher("Hello Rust Wasm!", "my-secret-key");
console.log("Encrypted:", secret);
}
run();
</script>
I love how this feels. I'm writing a high-performance byte-shuffling algorithm in Rust, but I'm consuming it as a simple async function in JavaScript. The overhead is minimal, and the type safety is preserved on the Rust side.
📋 Practical Task
Build a Wasm-Powered Prime Number Sequence Generator
Your task is to create a Rust library that compiles to WebAssembly. The library should export a function called generate_primes that takes a u32 (the number of primes to find) and returns a Vec<u32> containing that many prime numbers.
Requirements:
- Configure your
Cargo.tomlwith the correctcrate-typeto ensure it compiles as a Wasm library. - Use
wasm-bindgento export the function. - Build the project using
wasm-pack build --target web. - Create a basic
index.htmlfile that initializes the Wasm module and logs the first 10 primes to the browser console.
There are no comments for now.