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:
- super_saludo: toma un string y lo convierte a mayúsculas.
- al_reves: toma un string y lo devuelve al revés.
- sumar: suma dos numeros y devuelve el resultado de dicha operación.
// /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:
- my_wasm_library.d.ts
- my_wasm_library.js
- my_wasm_library_bg.wasm (nuestro archivo wasm 😄)
- my_wasm_library_bg.wasm.d.ts
- package.json
- README.md (wasm-pack copia el archivo README.md de la raiz del proyecto, en caso de tener uno).
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