Rust—web assembly 101 Link to heading

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