Skip to Content
Course content

68: Compiling Rust to WebAssembly

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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.toml with the correct crate-type to ensure it compiles as a Wasm library.
  • Use wasm-bindgen to export the function.
  • Build the project using wasm-pack build --target web.
  • Create a basic index.html file that initializes the Wasm module and logs the first 10 primes to the browser console.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.