Fibonacci sequence in Rust via JavaScript
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:
#[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:
[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. 😉
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
<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
:
// 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: