Sobre nós Guias Projetos Contactos
Админка
please wait

WebAssembly (Wasm) permite executar código Rust em browsers a uma velocidade próxima da nativa. Isto abre possibilidades para tarefas intensivas em computação, como jogos, processamento de imagem e criptografia, que o JavaScript não consegue tratar de forma eficiente. Este guia aborda a criação de módulos Wasm com Rust na perspetiva de um developer sénior.

Porquê Rust + WebAssembly

A combinação destaca-se porque:

  1. Desempenho: Velocidade próxima da nativa em browsers
  2. Segurança: As garantias do Rust transitam para Wasm
  3. Tamanho: Binários mais pequenos do que C/C++
  4. Interop: Integração perfeita com JavaScript
  5. Portabilidade: Corre em qualquer lugar onde o Wasm corre

Casos de uso: Jogos, processamento de áudio/vídeo, criptografia, simulações, parsers e qualquer trabalho intensivo em CPU.

Configuração

Instalar Rust

# Instalar Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Adicionar target Wasm
rustup target add wasm32-unknown-unknown
# Instalar wasm-pack (recomendado)
cargo install wasm-pack
# Instalar wasm-bindgen-cli (para builds manuais)
cargo install wasm-bindgen-cli

Estrutura do Projeto

# Criar novo projeto
cargo new --lib my-wasm
cd my-wasm
my-wasm/
├── Cargo.toml
├── src/
│ └── lib.rs
├── pkg/ # Gerado pelo wasm-pack
│ ├── my_wasm.js
│ ├── my_wasm_bg.wasm
│ └── package.json
└── www/ # Opcional: demo web
└── index.html

Configuração

Cargo.toml

[package]
name = "my-wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3" # Tipos de JavaScript
web-sys = "0.3" # APIs Web
# Para depuração com console.log
console_error_panic_hook = "0.1"
[dev-dependencies]
wasm-bindgen-test = "0.3"
[profile.release]
# Otimizar para tamanho
opt-level = "s"
lto = true

Exemplo Básico

Função Simples

src/lib.rs:

use wasm_bindgen::prelude::*;
// Chamado quando o módulo wasm é instanciado
#[wasm_bindgen(start)]
pub fn main() {
// Definir panic hook para melhores mensagens de erro
console_error_panic_hook::set_once();
}
// Exportar uma função simples
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
// Exportar função que recebe e devolve números
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// Exportar função que funciona com arrays
#[wasm_bindgen]
pub fn sum(numbers: &[i32]) -> i32 {
numbers.iter().sum()
}

Build

# Build para web (recomendado)
wasm-pack build --target web
# Build para bundlers (webpack, etc.)
wasm-pack build --target bundler
# Build para Node.js
wasm-pack build --target nodejs
# Build de release com otimizações
wasm-pack build --target web --release

Utilizar em HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Rust Wasm Demo</title>
</head>
<body>
<h1>Rust WebAssembly</h1>
<div id="output"></div>
<script type="module">
import init, { greet, add, sum } from './pkg/my_wasm.js';
async function run() {
// Initialize the Wasm module
await init();
// Call Rust functions
const greeting = greet("World");
console.log(greeting); // "Hello, World!"
const result = add(5, 3);
console.log("5 + 3 =", result); // 8
const total = sum(new Int32Array([1, 2, 3, 4, 5]));
console.log("Sum:", total); // 15
document.getElementById('output').textContent = greeting;
}
run();
</script>
</body>
</html>

Interop com JavaScript

Chamar JavaScript a partir de Rust

use wasm_bindgen::prelude::*;
// Importar funções JavaScript
#[wasm_bindgen]
extern "C" {
// console.log
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
// alert
fn alert(s: &str);
// Função JavaScript personalizada
#[wasm_bindgen(js_namespace = window)]
fn customCallback(value: i32);
}
#[wasm_bindgen]
pub fn rust_function() {
log("Called from Rust!");
alert("Hello from Rust!");
}

Trabalhar com o DOM

Ative as funcionalidades de web-sys em Cargo.toml:

[dependencies.web-sys]
version = "0.3"
features = [
"Document",
"Element",
"HtmlElement",
"Window",
"console",
]
use wasm_bindgen::prelude::*;
use web_sys::{Document, Element, Window};
#[wasm_bindgen]
pub fn manipulate_dom() -> Result<(), JsValue> {
let window: Window = web_sys::window().expect("no window");
let document: Document = window.document().expect("no document");
// Criar elemento
let element: Element = document.create_element("div")?;
element.set_inner_html("<h1>Created by Rust!</h1>");
element.set_attribute("class", "rust-created")?;
// Anexar ao body
let body = document.body().expect("no body");
body.append_child(&element)?;
Ok(())
}

Callbacks e Closures

use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn set_timeout_rust(callback: &js_sys::Function, delay: i32) {
let window = web_sys::window().expect("no window");
window.set_timeout_with_callback_and_timeout_and_arguments_0(
callback,
delay
).unwrap();
}
// Closure de Rust para JavaScript
#[wasm_bindgen]
pub fn create_callback() -> JsValue {
let closure = Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
web_sys::console::log_1(&format!("Clicked at: {}, {}", event.client_x(), event.client_y()).into());
}) as Box<dyn FnMut(_)>);
let js_value = closure.as_ref().clone();
closure.forget(); // Impedir que o Rust faça drop da closure
js_value
}

Structs e Classes

Exportar Struct como Class

use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Counter {
value: i32,
}
#[wasm_bindgen]
impl Counter {
#[wasm_bindgen(constructor)]
pub fn new(initial: i32) -> Counter {
Counter { value: initial }
}
pub fn increment(&mut self) {
self.value += 1;
}
pub fn decrement(&mut self) {
self.value -= 1;
}
pub fn get(&self) -> i32 {
self.value
}
pub fn set(&mut self, value: i32) {
self.value = value;
}
}

Utilização em JavaScript:

import init, { Counter } from './pkg/my_wasm.js';
await init();
const counter = new Counter(10);
console.log(counter.get()); // 10
counter.increment();
counter.increment();
console.log(counter.get()); // 12
counter.free(); // Limpar a memória do Wasm

Exemplo Prático: Processamento de Imagem

use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn grayscale(data: &mut [u8]) {
// Processar dados de píxeis RGBA
for pixel in data.chunks_exact_mut(4) {
let r = pixel[0] as f32;
let g = pixel[1] as f32;
let b = pixel[2] as f32;
// Método de luminosidade
let gray = (0.299 * r + 0.587 * g + 0.114 * b) as u8;
pixel[0] = gray;
pixel[1] = gray;
pixel[2] = gray;
// pixel[3] é alpha; deixar inalterado
}
}
#[wasm_bindgen]
pub fn invert(data: &mut [u8]) {
for pixel in data.chunks_exact_mut(4) {
pixel[0] = 255 - pixel[0];
pixel[1] = 255 - pixel[1];
pixel[2] = 255 - pixel[2];
}
}
#[wasm_bindgen]
pub fn brightness(data: &mut [u8], adjustment: i32) {
for pixel in data.chunks_exact_mut(4) {
pixel[0] = ((pixel[0] as i32 + adjustment).clamp(0, 255)) as u8;
pixel[1] = ((pixel[1] as i32 + adjustment).clamp(0, 255)) as u8;
pixel[2] = ((pixel[2] as i32 + adjustment).clamp(0, 255)) as u8;
}
}

Integração com JavaScript:

import init, { grayscale } from './pkg/my_wasm.js';
async function processImage() {
await init();
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Obter dados da imagem
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// Processar com Rust
grayscale(imageData.data);
// Voltar a colocar os dados processados
ctx.putImageData(imageData, 0, 0);
}

Testes

Testes Unitários

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
}

Testes Wasm

use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn test_greet() {
assert_eq!(greet("Test"), "Hello, Test!");
}
#[wasm_bindgen_test]
fn test_in_browser() {
let window = web_sys::window().expect("no window");
assert!(window.location().href().is_ok());
}

Executar testes:

# Executar testes unitários
cargo test
# Executar testes Wasm num browser headless
wasm-pack test --headless --chrome
wasm-pack test --headless --firefox

Otimização

Minimizar o Tamanho

[profile.release]
opt-level = "z" # Otimizar para tamanho
lto = true # Otimização em tempo de ligação
codegen-units = 1 # Unidade única de codegen
panic = "abort" # Abortar em panic
strip = true # Remover símbolos
# Usar wasm-opt para otimização adicional
wasm-pack build --release
# Ou manualmente
wasm-opt -Oz pkg/my_wasm_bg.wasm -o pkg/my_wasm_bg.wasm

Medir o Tamanho

# Verificar o tamanho do ficheiro
ls -lh pkg/*.wasm
# Analisar secções
wasm-objdump -h pkg/my_wasm_bg.wasm

Integração com Ferramentas de Build

Webpack

// webpack.config.js
module.exports = {
experiments: {
asyncWebAssembly: true
}
};
// Na sua app
import * as wasm from 'my-wasm';
wasm.greet("Webpack");

Vite

// vite.config.js
import wasm from 'vite-plugin-wasm';
export default {
plugins: [wasm()]
};

Principais Conclusões

  1. Use wasm-pack: Simplifica o build e o packaging
  2. Comece pequeno: Exporte primeiro funções simples
  3. Gestão de memória: Chame .free() em structs Rust
  4. Otimize para tamanho: Builds de release com as definições adequadas
  5. Teste no browser: Algumas APIs só funcionam no browser
  6. Escolha o target certo: web, bundler ou nodejs

WebAssembly com Rust abre novas possibilidades para o desempenho na web — tire partido disso para tarefas intensivas em computação que seriam demasiado lentas em JavaScript.

 
 
 
Языки
Темы
Copyright © 1999 — 2026
ZK Interactive