Working with WebAssembly and Rust: Advanced Integration Methods in Web

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Rust and WebAssembly at an Advanced Level

\n

WebAssembly (Wasm) and Rust are a combination that allows you to move computationally heavy tasks from JavaScript into high-performance native code running in the browser. At a basic level, you may have already encountered compiling simple functions. However, to create real products — game engines, image processors, parsers, or cryptographic libraries — a deep understanding of memory management, passing complex data structures, and interacting with the external environment is required.

\n

In this article, we will explore advanced techniques for working with WebAssembly in Rust: from manual memory management to creating hybrid applications where Rust and JavaScript work as a single unit. The material is aimed at developers already familiar with the basics of Rust and Wasm.

\n\n

1. Memory Management: Linear Memory and Allocators

\n

Unlike JavaScript, WebAssembly uses linear memory — a flat array of bytes. Rust code compiled to Wasm works with this memory directly. For efficient work, it is necessary to understand how to allocate and free memory manually.

\n\n

1.1. Manual Memory Allocation with wasm_bindgen

\n

The wasm-bindgen library provides convenient abstractions, but at an advanced level, direct memory access is often required. Consider the following example:

\n
use wasm_bindgen::prelude::*;\nuse std::mem;\nuse std::slice;\n\n#[wasm_bindgen]\npub fn allocate_buffer(size: usize) -> *mut u8 {\n    let mut buf = Vec::with_capacity(size);\n    let ptr = buf.as_mut_ptr();\n    mem::forget(buf); // prevent vector from being dropped\n    ptr\n}\n\n#[wasm_bindgen]\npub fn deallocate_buffer(ptr: *mut u8, size: usize) {\n    unsafe {\n        let _ = Vec::from_raw_parts(ptr, 0, size);\n    }\n}\n\n#[wasm_bindgen]\npub fn fill_buffer(ptr: *mut u8, value: u8, len: usize) {\n    let slice = unsafe { slice::from_raw_parts_mut(ptr, len) };\n    for byte in slice.iter_mut() {\n        *byte = value;\n    }\n}
\n

Here we allocate memory via Vec, "forget" about it (so Rust does not drop the buffer), and pass the pointer to JavaScript. The deallocate_buffer function restores the vector and correctly frees the memory.

\n\n

1.2. Using a Custom Allocator

\n

For complex projects, it is worth replacing the standard allocator with a more predictable one. For example, wee_alloc is a small allocator optimized for Wasm:

\n
// in Cargo.toml\n// [dependencies]\n// wee_alloc = "0.4"\n\n#[global_allocator]\nstatic ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
\n

This will reduce the size of the final .wasm file and speed up memory allocation in the browser.

\n\n

2. Passing Complex Data Structures Between Rust and JavaScript

\n

At an advanced level, you are not limited to numbers and strings. You need to pass arrays, structures, and even closures.

\n\n

2.1. Structures with #[wasm_bindgen]

\n

You can export entire Rust structures as JavaScript classes:

\n
#[wasm_bindgen]\npub struct Matrix {\n    rows: usize,\n    cols: usize,\n    data: Vec<f64>,\n}\n\n#[wasm_bindgen]\nimpl Matrix {\n    #[wasm_bindgen(constructor)]\n    pub fn new(rows: usize, cols: usize) -> Matrix {\n        Matrix {\n            rows,\n            cols,\n            data: vec![0.0; rows * cols],\n        }\n    }\n\n    pub fn get(&self, row: usize, col: usize) -> f64 {\n        self.data[row * self.cols + col]\n    }\n\n    pub fn set(&mut self, row: usize, col: usize, val: f64) {\n        self.data[row * self.cols + col] = val;\n    }\n\n    pub fn multiply(&self, other: &Matrix) -> Result<Matrix, String> {\n        if self.cols != other.rows {\n            return Err("Incompatible dimensions".to_string());\n        }\n        // ... matrix multiplication implementation\n        let result = Matrix::new(self.rows, other.cols);\n        Ok(result)\n                

Blogs

Book Recommendations