Rust WASM hello world – no need for webpack!

Up to now I’ve been following the official guide and using webpack to package my Rust+WASM code to run in a browser.

But today I found out there is no need for webpack at all! This makes development much faster, with many fewer dependencies.

Setup

Before you start:

Create a project

cargo new --lib smol-but-rusty

This will create a new project. Edit its Cargo.toml to look like this:

[package]
name = "smol-but-rusty"
version = "0.1.0"
edition = "2021"

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

[dependencies]
wasm-bindgen = "0.2.63"

Write your code

Change src/lib.rs to look like this:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn say_hello() -> String {
    "Hello, world!".to_owned()
}

Any function you want to call from JavaScript needs to be annotated like this. (Note: there are lots of restrictions on what types you can accept and return, to make the WASM compatible with JavaScript.)

Build it

cd smol-but-rusty
cargo build
wasm-pack build --target=web

This will create a pkg directory containing your compiled WASM and wrappers.

Refer to it from HTML

Create a new directory called www. Inside, create a file index.html and make it look like this:

<!DOCTYPE html>
<html>
  <body>
    <script type="module">
      import init, { say_hello } from './smol_but_rusty.js';

      async function run() {
        await init();
        const p = document.createElement("p");
        p.innerText = say_hello();
        document.body.appendChild(p);
      }

      run();
    </script>
  </body>
</html>

The code that calls say_hello is calling the Rust function you wrote from JavaScript. It has been compiled into WASM in pkg/smol_but_rusty_bg.wasm, and wrapped by some JavaScript in pkg/smol_but_rusty.js.

Combine WASM with HTML

We placed index.html outside pkg because pkg is for generated stuff, and index.html is hand-coded.

Now we combine them together with:

cp www/* pkg/

Serve it

To see this in a web browser, serve it through a web server. For example, if you’ve got Python3:

cd pkg
python3 -m http.server

Now visit http://0.0.0.0:8000/ and if you’re lucky you’ll see this:

If not, check the steps above looking out for any error messages, and press F12 in your browser and click Console to see any errors in there.

No webpack!

No webpacks were used in the making of this code. Just Rust compiled to WASM and imported into a modern web browser.

You can write tests as normal and run them with cargo test. You can also run your tests in the browser(!) with wasm-pack test --firefox --headless if you have wasm-bindgen-test = "0.3.13" in your [dev-dependencies] in Cargo.toml.

You can write any Rust code you like, so long as the functions annotated with #[wasm_bindgen] work the way they need to for JavaScript compatibility.

You can also add lots of pure-Rust dependencies in Cargo.toml. Most things will compile without problems to WASM, and if they don’t, sometimes you can enable a feature that makes them work.

You probably want to update your Cargo.toml to include some of the good stuff in the example Cargo.toml, including optimising for size (opt-level = "s"), setting up a custom console_error_panic_hook to make the errors better, and maybe even the smaller allocator wee_alloc.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.