En los últimos meses, WebAssembly ha ganado tracción como una alternativa de alto rendimiento para ejecutar código nativo en el navegador. Si ya trabajas con Rust, ahora puedes compilar tus librerías a Wasm y consumirlas directamente desde JavaScript, sin depender de plugins o transpiladores pesados.

Ilustración por IA

En este tutorial vamos a crear una aplicación mínima que muestra un contador implementado en Rust y expuesto como módulo WebAssembly. Veremos cómo configurar el entorno, compilar el código, integrarlo en una página web y desplegarlo de forma estática.

¿Qué es WebAssembly?

WebAssembly (Wasm) es un formato binario portátil que permite ejecutar código de bajo nivel en navegadores modernos. Su diseño está pensado para ser rápido, seguro y compatible con el sandbox del navegador. Puedes consultar la documentación oficial en MDN Web Docs.

Configuración del proyecto

Comenzaremos creando una carpeta de proyecto y configurando Rust con wasm-pack, la herramienta recomendada para empaquetar crates Rust como módulos WebAssembly.

mkdir contador-wasm
cd contador-wasm
cargo init --lib
cargo install wasm-pack

Una vez instalado, inicializa el paquete con la configuración adecuada:

wasm-pack new --target web contador

Ilustración por IA

Compilación de Rust a Wasm

Edita el archivo src/lib.rs para exponer una función que incremente un contador.

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn incrementar(valor: i32) -> i32 {
    valor + 1
}

Asegúrate de añadir wasm-bindgen como dependencia en Cargo.toml:

[dependencies]
wasm-bindgen = "0.2"

Compila el crate con wasm-pack:

wasm-pack build --target web

El comando generará una carpeta pkg con los archivos .wasm, .js y los tipos TypeScript necesarios.

Integración con JavaScript

En la parte cliente, crea un archivo index.html que cargue el módulo generado y lo utilice para actualizar la UI.

<!DOCTYPE html>
<html lang='es'>
<head>
  <meta charset='UTF-8'>
  <title>Contador WebAssembly</title>
</head>
<body>
  <h1>Contador con Rust y WebAssembly</h1>
  <button id='boton'>Incrementar</button>
  <span id='valor'>0</span>
  <script type='module'>
    import init, { incrementar } from './pkg/contador.js';
    await init();
    const btn = document.getElementById('boton');
    const span = document.getElementById('valor');
    btn.addEventListener('click', () => {
      const actual = Number(span.textContent);
      span.textContent = incrementar(actual);
    });
  </script>
</body>
</html>

El módulo se carga mediante import y la función incrementar se invoca directamente desde el código JavaScript, manteniendo una interfaz sencilla.

Foto por Chris Ried en Unsplash

Despliegue y buenas prácticas

Para servir el archivo .wasm con los encabezados correctos, utiliza un servidor estático que incluya Content-Type: application/wasm. Por ejemplo, con http-server:

npx http-server . -c-1

Considera habilitar la caché de los módulos y usar integrity hashes para mayor seguridad. Además, revisa la compatibilidad con navegadores mediante la tabla de soporte de caniuse.com.

Conclusión

Hemos recorrido todo el flujo: desde la escritura de lógica en Rust, pasando por la compilación a WebAssembly, hasta su consumo en una aplicación web. Esta cadena permite aprovechar el rendimiento de Rust sin abandonar el ecosistema JavaScript, abriendo la puerta a casos de uso como videojuegos, procesamiento de imágenes o algoritmos intensivos.

Referencias