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
85: Building a Simple WebAssembly Module
Imagine you're running a busy restaurant. Your front-of-house manager (JavaScript) is great at multitasking—they handle the customers, take orders, and keep the vibe right. But when it comes to the actual cooking, the manager doesn't touch the stove. Instead, they hand a ticket to a specialist chef (WebAssembly) in the kitchen. The chef doesn't care about the customers or the decor; they just take raw ingredients, perform a highly optimized series of actions, and slide a finished plate back through the window. The manager doesn't need to know how the chef chops the onions; they just need to know how to send the order and receive the food.
In this scenario, JavaScript is your manager. It's flexible and manages the UI, but it can struggle with heavy computation. Rust is our specialist chef. By compiling Rust to WebAssembly (Wasm), we create a binary module that the browser can execute at near-native speed. The "window" where the ticket and plate are exchanged is handled by a tool called wasm-bindgen, which translates Rust's types into something JavaScript can understand.
Setting Up the Specialist's Kitchen
Before we write any code, you'll need wasm-pack. I've used a few different toolchains for Wasm, but wasm-pack is the gold standard because it handles the compilation to .wasm and generates the JavaScript "glue" code automatically. If you haven't installed it yet, go grab it now.
Your Cargo.toml needs two things: the cdylib crate type (which tells Rust to create a dynamic system library for Wasm) and the wasm-bindgen dependency. I usually put wasm-bindgen in the dependencies list like this:
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
Writing High-Performance Logic
Let's build something more substantial than a greeting. We'll create a prime number checker. While JS can do this, a heavy loop checking millions of numbers is exactly where Rust's efficiency shines. I want this function to be accessible from the browser, so we use the #[wasm_bindgen] attribute.
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn is_prime(n: u32) -> bool {
if n <= 1 { return false; }
if n <= 3 { return true; }
if n % 2 == 0 || n % 3 == 0 { return false; }
let mut i = 5;
while i * i <= n {
if n % i == 0 || n % (i + 2) == 0 {
return false;
}
i += 6;
}
true
}
Notice that I used u32. WebAssembly is very strict about types. When this compiles, wasm-bindgen will make sure that when JavaScript passes a number, it's treated as an unsigned 32-bit integer inside the Rust module.
Bridging the Gap to the Browser
Now, the magic happens during the build process. You don't just run cargo build; you run wasm-pack build --target web. This creates a pkg/ directory containing the compiled .wasm file and a JavaScript wrapper.
I've found that many people get tripped up here: you can't just include a Wasm file in a <script> tag. You have to initialize the module. Here is how you'd actually use our "Chef" in a simple HTML file:
<script type="module">
import init, { is_prime } from './pkg/prime_checker.js';
async function run() {
// This initializes the wasm module
await init();
const num = 7919;
console.log(`Is ${num} prime? ${is_prime(num)}`);
}
run();
</script>
The init() function is crucial. It fetches the .wasm binary and compiles it into the browser's memory. Once that's done, is_prime is just a regular JavaScript function that happens to be executing Rust code under the hood.
📋 Practical Task
Building a Fibonacci Sequence Generator for the Browser
Your task is to expand on the prime checker logic to create a WebAssembly module that calculates Fibonacci numbers. This is a classic example of a recursive or iterative process that benefits from Rust's speed as the input numbers grow.
- Create a new Rust library project and configure it for
cdylib. - Implement a function
fibonacci(n: u32) -> u32using the#[wasm_bindgen]attribute. - Ensure the function can handle
n = 0andn = 1correctly. - Use
wasm-pack build --target webto compile the module. - Create a minimal HTML page that imports the generated JS glue code, initializes the module, and logs the 40th Fibonacci number to the console.
There are no comments for now.