Rust y WebAssembly

5 min(s) Fecha: 2022-02-06

Rust y WebAssembly, un ejemplo práctico.

WebAssembly abreviado Wasm, es un formato de codigo binario portable (bytecode). Wasm está diseñado como un objetivo de compilación portátil para lenguajes de programación, lo que permite el despliegue en la web de aplicaciones de cliente y servidor.

y en este caso usaremos Rust 🦀


Requisitos

instalamos wasm-pack

cargo install wasm-pack

Empezando

Le decimos a cargo que necesitamos crear una librería:

cargo new my-wasm-library --lib

En nuestro archivo de configuración de cargo, agregaremos lo siguiente:

# Cargo.toml
[package]
edition = "2018"

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

[dependencies]
wasm-bindgen = "0.2.33"

cdylib le indica al compilador de Rust que se producirá una libreria de enlace dinámico. Esto se utiliza cuando se compila una biblioteca dinámica para ser cargada desde otro lenguaje.

Este tipo de salida creará archivos *.so en Gnu/Linux, archivos *.dylib en macOS y archivos *.dll en Windows.

wasm-bindgen es el crate que facilita las interacciones de alto nivel entre los módulos Wasm y JavaScript.


Desarrollando nuestra librería en Rust

Incluimos al inicio el atributo use wasm_bindgen::prelude::wasm_bindgen; de wasm_bindgen al espacio de nombres actual.

Luego creamos tres funciones:

// /src/lib.rs
use wasm_bindgen::prelude::wasm_bindgen;

#[wasm_bindgen]
pub fn super_saludo(original: &str) -> String {
	format!("HELLO, {}", original.to_uppercase())
}

#[wasm_bindgen]
pub fn al_reves(s: String) -> String {
	s.chars().rev().collect::<String>()
}

#[wasm_bindgen]
pub fn sumar(x: u32, y: u32) -> u32 {
	x + y
}

Arriba de cada declaración de función agregamos #[wasm_bindgen] Este atributo hace posible llamar a Rust desde JavaScript. 🎉

Ademas genera código que puede convertir los tipos básicos que wasm entiende (enteros y flotantes) en tipos más complejos como cadenas y viceversa. Si te interesa saber cómo funciona, échale un ojo a esto.


Compilando nuestro código a WebAssembly

wasm-pack pretende ser una ventanilla única para construir y trabajar con WebAssembly generado por Rust.

wasm-pack te ayuda a construir paquetes de WebAssembly de una manera muy rápida y sencilla. Puedes encontrar más información por aquí.

Compilamos nuestro código a webassembly...

wasm-pack build --release

y la salida...

 wasm-pack build --release
[INFO]: Checking for the Wasm target...
[INFO]: Compiling to Wasm...
	 Compiling my-wasm-library v0.1.0 (/home/awesome-user/dev/my-wasm-library)
		Finished release [optimized] target(s) in 4.14s
[INFO]: License key is set in Cargo.toml but no LICENSE file(s) were found; Please add the LICENSE file(s) to your project directory
[INFO]: Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: :-) Done in 6.67s
[INFO]: :-) Your wasm pkg is ready to publish at /home/awesome-user/dev/my-wasm-library/pkg.

Si todo sale bien, esto creará varios archivos en una carpeta llamada pkg:

Si se desea cambiar la carpeta de destino, utiliza:

wasm-pack build --release --target TARGET -d dest-folder

ejemplo:

wasm-pack build --release --target web -d dist-web

Utilizando wasm en la web

Una vez se ha generado el archivo wasm, creamos un archivo html (ej: index.html) para nuestro ejemplo:

<!-- index.html -->
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title>hello WebAssembly ES6 example</title>
	</head>
	<body>
		<script type="module">
			import init, {
				super_saludo,
				al_reves,
				sumar,
			} from "./my_wasm_library.js";

			init().then(() => {
				let greeting = super_saludo("WebAssembly");
				console.log(greeting);

				let reverseString = al_reves("WebAssembly al reves");
				console.log(reverseString);
				
				let resultAdd = sumar(1,2);
				console.log(resultAdd);
			});
		</script>
	</body>
</html>
Sirve el directorio raíz del proyecto (la carpeta de destino) con un servidor web local. Ya que no funcionará si lanzas el archivo html directamente en el navegador.
-- un visitante del blog

Aquí vemos que podemos hacer import de las funciones generadas a webassembly:

import init, {super_saludo, al_reves, sumar} from "./my_wasm_library.js";

init().then(() => {
	let greeting = super_saludo("WebAssembly");
	console.log(greeting);

	let reverseString = al_reves("WebAssembly al reves");
	console.log(reverseString);

	let resultAdd = sumar(1,2);
	console.log(resultAdd);
});

En la consola del nevegador deberás observar algo como:

(index):17 HELLO, WEBASSEMBLY
(index):20 sever la ylbmessAbeW
(index):23 3

Referencias