Skip to content

Fibonacci sequence in Rust via JavaScript

Open in GitHub Codespaces

We're going to compile some Rust code to WebAssembly and then run it in the browser! 🤩 Make sure that you have the wasm32-unknown-unknown target installed for your Rust toolchain. If you're using rustup you can use rustup target add wasm32-unknown-unknown to install the target.

Here's the lib.rs that we're going to compile into WebAssembly:

rs
#[no_mangle]
pub fn fib(n: u32) -> u32 {
    if n <= 1 {
        return n;
    } else {
        return fib(n - 1) + fib(n - 2);
    }
}

💡 Need a quick refreshed on Rust? Check out Rust by Example!

❓ What happens if we omit the #[no_mangle] macro? Rust will treat the symbol as not exported and optimize it away leaving us with no fib() export to use from our JavaScript host. 😢

Now that we have our Rust code written, there's one setting we need to tweak in our Cargo.toml so that we can generate a WebAssembly module:

toml
[package]
name = "fib"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

🎗️ Make sure that you set the crate-type = ["cdylib"] option! It signifies that you'd like the compiler to create a dynamic system library. For the WebAssembly target that means "create a *.wasm file without a start function". On other platforms this output type will create _.so file on Linux, _.dylib on macOS, and _.dll Windows.

✨ You can quickly create a new Cargo Rust project using cargo new.

Now that you have the project set up you can build it using cargo build. Just make sure that you set the --target to wasm32-unknown-unknown! Otherwise you'll just end up with a cdylib library for your current operating system instead of a fib.wasm file. 😉

sh
cargo build --target wasm32-unknown-unknown

Now that we have our fib.wasm file we can run it in the browser and call the fib() function to run that Fibonacci calculation in our WebAssembly code.

index.html
html
<script type="module" src="index.js"></script>
<p>Check the DevTools console!</p>

🚀 Here's the JavaScript code that will be run via index.html:

js
// 1. Load the binary contents of the WebAssembly file.
///   If you were in Node.js you'd use 'fs.readFile()'.
const response = await fetch("target/wasm32-unknown-unknown/debug/fib.wasm");
const buffer = await response.arrayBuffer();

// 2. Compile the WebAssembly blob into a 'WebAssembly.Module'.
//    This will throw an error if the buffer isn't valid WebAssembly.
const module = await WebAssembly.compile(buffer);

// 3. Instantiate the module with any imports. We need none.
//    Normally you'd pass in things like a 'println()' function.
const importObject = {};
const instance = await WebAssembly.instantiate(module, importObject);

// 4. Call the exported 'fib()' function!
const { fib } = instance.exports;
console.log("fib(10):", fib(10));
console.log("fib(15):", fib(15));

😉 We're deliberately not using the recommended WebAssembly.instantiateStreaming() function so that you can see the steps individually.

ℹ Since we aren't using WASI or any other functionality from the outside world we don't need any imports in our importObject.

Now we can start up an HTTP server with python -m http.server or your other favorite static HTTP server and see the results: