Algoritmos y Estructuras de Datos Herramientas Lenguaje de programación
!Prog C/C++ Rust
Linux Matemáticas
Mates Discretas
Programación Orientada a Objetos Sistemas Operativos

WASM

[date: 30-07-2021 00:00] [last modification: 16-01-2023 15:05]
[words: 419] [reading time: 2min] [size: 31434 bytes]

Breve guía por pasos de cómo compilar Rust para Web Assembly (WASM)

Rust y WebAssembly

Sin nada

  1. Añadir wasm al compilador:

    1rustup component add wasm32-unknown-unknown
    
  2. Cargo.toml

    1# ...
    2
    3# Con esto, no se pueden llamar funciones de `lib.rs` desde un
    4# binario como `main.rs`
    5[lib]
    6crate-type = ["cdylib"]
    
  3. lib.rs

    1// No cambia el nombre de la función para unirla más fácil
    2#[no_mangle]
    3pub fn double(number: i32) -> i32 {
    4    2 * number
    5}
    
  4. Compilar:

    1cargo build --target wasm32-unknow-unknown
    
  5. Script que cargará y ejecutará la función:

     1async function load_wasm(path) {
     2    const {instance} = await WebAssembly.getinstantiateStreaming(fetch(path));
     3
     4    if (instance) {
     5        return instance;
     6    } else {
     7        throw `Error loading ${path}: instance is undefined`;
     8    }
     9}
    10
    11function main() {
    12    const wasm = load_wasm("./target/wasm32-unknown-unknown/debug/<nombre>.wasm")
    13    console.log(wasm.double(2)); // Se imprimirá 2
    14}
    
  6. La página web se tendrá que mostrar en un servidor por temas de seguridad:

    1python -m http.server
    

Con WASM-PACK y WASM-BINDGEN

  1. Descargué wasm_pack, porque cargo install wasm-pack daba error de compilación.

  2. Escribí un archivo por lotes que crea un servidor en python y abre la página en Chrome, porque por temas de seguridad, los navegadores no permiten acceso a archivos (como el .wasm).

    1@echo off
    2wasm-pack build --target web
    3REM Abre la pagina primero porque el servidor detendrá el programa
    4start chrome http://localhost:8000
    5python -m http.server
    
  3. Cargo.toml y .gitignore

     1[package]
     2name = "<nombre>"
     3# ...
     4
     5# Con esto, no se pueden llamar funciones de `lib.rs` desde un
     6# binario como `main.rs`
     7[lib]
     8crate-type = ["cdylib"]
     9
    10[dependencies]
    11wasm-bindgen = "*"
    12
    13# Opcional, pero es buena idea hacer que `release` se
    14# centre en hacerlo pequeño para enviarlo por internet
    15[profile.release]
    16opt-level = "s"
    
    1/target
    2/pkg
    
  4. Para llamar funciones de JavaScript desde Rust se usa:

     1use wasm_bindgen::prelude::*;
     2
     3#[wasm-bingen]
     4extern {
     5    // La función. Ejemplo:
     6    fn alert(msg: &str);
     7}
     8
     9// Esta función se puede llamar desde JavaScript
    10#[wasm-bingen]
    11fn greet(name: &str) {
    12    // Se queja porque el `unsafe` es innecesario
    13    // Pero si lo quito lo marca como error y no corrige otros errores
    14    // Sin embargo, no produce ningún error al compilar
    15    alert(&format("Hello {}", name));
    16}
    
  5. El HTML básico (el nombre no acaba en bg):

     1<!DOCTYPE html>
     2<html>
     3<body>
     4    <!-- `type="module"` es para no tener que escribir un `.js` por
     5    separado, ya que `import` solo funciona en un módulo -->
     6    <script type="module">
     7        import init, {<nombre_función>} from "./pkg/<nombre>.js";
     8
     9        init()
    10            .then(() => {
    11                // ...
    12            });
    13    </script>
    14</body>
    15</html>
    
  6. Exceptuando el archivo .wasm y <nombre>.js, son todos innecesarios.

Anterior: Testing Volver a Rust