Rust—web assembly 101

In this article, we will use Rust to build and compile into web assembly and execute from the browser.

Here is what we will do. A user will drag and drop a text file to the web browser. Then we use Rust to convert to upper case letters via web-assembly, and toss back to the user. Obviously, this upper case operation is just for demonstration purposes, and one would replace it with a more complex function.

Below is how the page will look like. There is a drag and drop box, and the Save as button will appear when the converted file is ready. So, let’s do this.

Rust—web assembly 101

First, we need wasm-pack crate

cargo install wasm-pack

Next, let’s create a cargo for this project

$ cargo new --lib hello-wasm
$ cd hello-wasm

Then, modify src/lib.rs file to add the Rust function

// This is the Rust code that is compiled to WebAssembly
use wasm_bindgen::prelude::*;

// This function takes a slice of bytes and returns a new vector of bytes with all letters capitalized
#[wasm_bindgen]
pub fn to_uppercase(bytes: &[u8]) -> Vec<u8> {
    // Convert the bytes to a string
    let s = String::from_utf8_lossy(bytes);

    // Capitalize all letters in the string
    let upper_s = s.to_uppercase();

    // Convert the string back to a vector of bytes and return it
    upper_s.into_bytes()
}

We also need to add dependencies to Cargo.toml

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

[dependencies]
wasm-bindgen = "0.2"

Now, we create the web assembly package

$ wasm-pack build --target web

Finally, we create index.html page

<!DOCTYPE html>
<html>

<head>
  <title>Drag and Drop File</title>
  <style>
    #drop-zone {
      width: 300px;
      height: 200px;
      border: 2px dashed #ccc;
      text-align: center;
      padding: 40px;
      font-size: 20px;
    }
  </style>
</head>

<body>
  <div id="drop-zone">
    <p>Drag and drop a file here</p>
  </div>
  <button id="save-button" style="display: none;">Save as</button>

  <script type="module">
    import init, { to_uppercase } from './pkg/hello_wasm.js';

    async function run() {
      await init();

      const dropZone = document.getElementById('drop-zone');
      const saveButton = document.getElementById('save-button');

      dropZone.addEventListener('dragover', (e) => {
        e.preventDefault();
        dropZone.style.backgroundColor = '#f2f2f2';
      });

      dropZone.addEventListener('dragleave', (e) => {
        e.preventDefault();
        dropZone.style.backgroundColor = 'transparent';
      });

      dropZone.addEventListener('drop', (e) => {
        e.preventDefault();
        dropZone.style.backgroundColor = 'transparent';
        const file = e.dataTransfer.files[0];
        const reader = new FileReader();

        reader.onload = (e) => {
          const content = e.target.result;

          // Convert the content to a Uint8Array
          const bytes = new Uint8Array(content);

          // Call the Rust function that converts all letters to capital letters
          const upperBytes = to_uppercase(bytes);

          // Convert the Uint8Array back to a string
          const upperText = new TextDecoder().decode(upperBytes);

          // Create a download link with the converted text and the original file name
          const downloadLink = document.createElement('a');
          downloadLink.href = URL.createObjectURL(new Blob([upperText], { type: 'text/plain' }));
          downloadLink.download = file.name;

          // Show the save button and add a click event listener
          saveButton.style.display = 'block';
          saveButton.addEventListener('click', () => {
            downloadLink.click();
          });
        };

        // Read the file as an array buffer
        reader.readAsArrayBuffer(file);

      });
    }

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

</html>

Alright, let’s run it and see

$ python3 -m http.server

Try to drag a text file into the drag and drop box. A Save as button should appear, from which you can download the capitalized text file!

Update: Rodney James shared that Rustc 1.70.0 or higher is required to follow this exercise.

Compiling from Rust to WebAssembly - WebAssembly | MDN

The wasm-bindgen Guide