Trabajo con WebAssembly y Rust: métodos avanzados de integración en la web

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: por qué Rust y WebAssembly a nivel avanzado

\n

WebAssembly (Wasm) y Rust son una combinación que permite trasladar tareas computacionalmente pesadas desde JavaScript a código nativo de alto rendimiento que se ejecuta en el navegador. A nivel básico, es posible que ya te hayas familiarizado con la compilación de funciones simples. Sin embargo, para crear productos reales —motores de juegos, procesadores de imágenes, analizadores sintácticos o bibliotecas criptográficas— se requiere una comprensión profunda de la gestión de memoria, la transferencia de estructuras de datos complejas y la interacción con el entorno externo.

\n

En este artículo, exploraremos técnicas avanzadas para trabajar con WebAssembly en Rust: desde la gestión manual de memoria hasta la creación de aplicaciones híbridas donde Rust y JavaScript funcionan como una sola unidad. El material está dirigido a desarrolladores que ya conocen los fundamentos de Rust y Wasm.

\n\n

1. Gestión de memoria: memoria lineal y asignadores

\n

A diferencia de JavaScript, WebAssembly utiliza memoria lineal — un arreglo plano de bytes. El código Rust compilado a Wasm trabaja directamente con esta memoria. Para un trabajo eficiente, es necesario comprender cómo asignar y liberar memoria manualmente.

\n\n

1.1. Asignación manual de memoria con wasm_bindgen

\n

La biblioteca wasm-bindgen proporciona abstracciones convenientes, pero a nivel avanzado a menudo se requiere acceso directo a la memoria. Veamos un ejemplo:

\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); // evitamos la eliminación del vector\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

Aquí asignamos memoria a través de Vec, "olvidamos" el vector (para que Rust no elimine el búfer) y pasamos el puntero a JavaScript. La función deallocate_buffer restaura el vector y libera la memoria correctamente.

\n\n

1.2. Uso de un asignador personalizado

\n

Para proyectos complejos, vale la pena reemplazar el asignador estándar por uno más predecible. Por ejemplo, wee_alloc es un asignador pequeño optimizado para Wasm:

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

Esto reducirá el tamaño del archivo .wasm final y acelerará la asignación de memoria en el navegador.

\n\n

2. Transferencia de estructuras de datos complejas entre Rust y JavaScript

\n

A nivel avanzado, no te limitas a números y cadenas. Es necesario transferir arreglos, estructuras e incluso clausuras.

\n\n

2.1. Estructuras con #[wasm_bindgen]

\n

Puedes exportar estructuras Rust completas como clases de JavaScript:

\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("Dimensiones incompatibles".to_string());\n        }\n        // ... implementación de multiplicación de matrices\n        let result = Matrix::new(self.rows, other.cols);\n        Ok(result)\n                

Blogs

Book Recommendations