WebAssembly (Wasm) é um formato binário que permite executar código de alta performance diretamente no navegador (e fora dele). A crate wasm-bindgen é a ponte que conecta Rust ao mundo JavaScript, permitindo que funções Rust sejam chamadas de JS e vice-versa, com conversão automática de tipos.
Rust é uma das linguagens mais bem posicionadas para WebAssembly: sem garbage collector, sem runtime pesado, com controle fino de memória e performance próxima ao código nativo. Com wasm-bindgen, web-sys e wasm-pack, você tem um ecossistema completo para criar desde bibliotecas de processamento de dados até aplicações web interativas.
wasm-bindgen Rust em 2026: onde ele encaixa
Quem busca wasm-bindgen Rust normalmente quer responder uma pergunta prática: como levar código Rust para uma aplicação web sem reescrever todo o frontend? A resposta curta é que wasm-bindgen funciona melhor como camada de interoperabilidade. Você mantém React, Vue, Svelte, JavaScript ou TypeScript onde eles são fortes, e move para Rust as partes que precisam de performance previsível, segurança de memória, processamento local ou reutilização de uma biblioteca existente.
Os casos mais comuns são filtros de imagem, compressão, criptografia, simulação, parsers, editores, visualização de dados, plugins e ferramentas que rodam no browser. Para uma visão ampla do tema, veja também Rust e WebAssembly em 2026 e o tutorial passo a passo de WebAssembly com Rust. Se o foco for aplicação web de alta performance, o guia Rust e WebAssembly: apps web de alta performance complementa esta referência com exemplos de produto.
Para carreira, o sinal forte não é “usar WASM em tudo”. O sinal forte é saber explicar quando Rust no browser vale a complexidade: gargalos reais, código compartilhado, fronteira clara com JavaScript, testes, bundle size, observabilidade e fallback. Combine wasm-bindgen com Serde para contratos de dados, Criterion para benchmark, Cargo para empacotamento e Clippy para manter o módulo confiável. Depois conecte isso a um projeto de portfólio e acompanhe as vagas Rust e empresas que usam Rust em áreas como segurança, fintech, ferramentas para devs, dados e edge computing.
Instalação
Pré-requisitos
Instale as ferramentas necessárias:
# Target de compilação WASM
rustup target add wasm32-unknown-unknown
# wasm-pack: ferramenta de build e publicação
cargo install wasm-pack
# Opcional: servidor de desenvolvimento
cargo install miniserve
Criando um Projeto WASM
# Usando wasm-pack template
cargo init meu-projeto-wasm --lib
cd meu-projeto-wasm
Configure o Cargo.toml:
[package]
name = "meu-projeto-wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = { version = "0.3", features = [
"console",
"Document",
"Element",
"HtmlElement",
"HtmlCanvasElement",
"CanvasRenderingContext2d",
"Window",
"Node",
"Event",
"MouseEvent",
"KeyboardEvent",
"HtmlInputElement",
"CssStyleDeclaration",
"DomTokenList",
] }
[profile.release]
opt-level = "s" # Otimizar para tamanho
lto = true # Link-Time Optimization
Uso Básico
Exportando Funções para JavaScript
// src/lib.rs
use wasm_bindgen::prelude::*;
// Exportar uma função simples
#[wasm_bindgen]
pub fn somar(a: i32, b: i32) -> i32 {
a + b
}
// Exportar com nome diferente em JS
#[wasm_bindgen(js_name = cumprimentar)]
pub fn saudar(nome: &str) -> String {
format!("Olá, {}! Bem-vindo ao Rust + WASM!", nome)
}
// Função que recebe e retorna tipos complexos
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> Vec<u32> {
let mut seq = vec![0, 1];
for i in 2..n as usize {
let proximo = seq[i - 1] + seq[i - 2];
seq.push(proximo);
}
seq
}
// Função que pode falhar
#[wasm_bindgen]
pub fn dividir(a: f64, b: f64) -> Result<f64, JsValue> {
if b == 0.0 {
Err(JsValue::from_str("Divisão por zero!"))
} else {
Ok(a / b)
}
}
Compilando e Usando
# Compilar com wasm-pack
wasm-pack build --target web
# Isso gera a pasta pkg/ com:
# - meu_projeto_wasm_bg.wasm (binário WASM)
# - meu_projeto_wasm.js (glue code JS)
# - meu_projeto_wasm.d.ts (tipos TypeScript)
# - package.json
Usando no HTML:
<!DOCTYPE html>
<html>
<head>
<title>Rust + WASM</title>
</head>
<body>
<h1>Rust WebAssembly</h1>
<div id="resultado"></div>
<script type="module">
import init, { somar, cumprimentar, fibonacci, dividir }
from './pkg/meu_projeto_wasm.js';
async function main() {
await init();
console.log("2 + 3 =", somar(2, 3));
console.log(cumprimentar("Maria"));
console.log("Fibonacci:", fibonacci(10));
try {
console.log("10 / 3 =", dividir(10, 3));
console.log("10 / 0 =", dividir(10, 0));
} catch (e) {
console.error("Erro:", e);
}
document.getElementById("resultado").textContent =
cumprimentar("Mundo");
}
main();
</script>
</body>
</html>
Importando Funções JavaScript
use wasm_bindgen::prelude::*;
// Importar funções globais do JS
#[wasm_bindgen]
extern "C" {
// window.alert()
fn alert(s: &str);
// console.log()
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_u32(a: u32);
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_many(a: &str, b: &str);
// performance.now()
#[wasm_bindgen(js_namespace = performance)]
fn now() -> f64;
}
// Macro auxiliar para console.log
macro_rules! console_log {
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
#[wasm_bindgen]
pub fn demonstrar_imports() {
console_log!("Olá do Rust via console.log!");
console_log!("Performance.now(): {:.2}ms", now());
log_many("Múltiplos", "argumentos");
}
Structs Exportadas
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct Contador {
valor: i32,
historico: Vec<i32>,
}
#[wasm_bindgen]
impl Contador {
#[wasm_bindgen(constructor)]
pub fn novo(valor_inicial: i32) -> Contador {
Contador {
valor: valor_inicial,
historico: vec![valor_inicial],
}
}
pub fn incrementar(&mut self) {
self.valor += 1;
self.historico.push(self.valor);
}
pub fn decrementar(&mut self) {
self.valor -= 1;
self.historico.push(self.valor);
}
pub fn valor(&self) -> i32 {
self.valor
}
pub fn resetar(&mut self, valor: i32) {
self.valor = valor;
self.historico.clear();
self.historico.push(valor);
}
pub fn historico_json(&self) -> String {
format!("{:?}", self.historico)
}
}
Uso em JavaScript:
import { Contador } from './pkg/meu_projeto_wasm.js';
const contador = new Contador(0);
contador.incrementar();
contador.incrementar();
contador.incrementar();
contador.decrementar();
console.log("Valor:", contador.valor()); // 2
console.log("Histórico:", contador.historico_json()); // [0, 1, 2, 3, 2]
contador.free(); // Liberar memória WASM
Recursos Avançados
web-sys: APIs do Navegador
A crate web-sys fornece bindings para todas as Web APIs:
use wasm_bindgen::prelude::*;
use web_sys::{console, Document, Element, HtmlElement, Window};
fn window() -> Window {
web_sys::window().expect("Sem window global")
}
fn document() -> Document {
window().document().expect("Sem document")
}
#[wasm_bindgen]
pub fn manipular_dom() -> Result<(), JsValue> {
let document = document();
// Criar elemento
let div = document.create_element("div")?;
div.set_id("meu-div");
div.set_class_name("container ativo");
div.set_inner_html("<h2>Criado pelo Rust!</h2><p>Manipulação de DOM via wasm-bindgen.</p>");
// Estilizar
let estilo = div
.dyn_ref::<HtmlElement>()
.unwrap()
.style();
estilo.set_property("background-color", "#f0f0f0")?;
estilo.set_property("padding", "20px")?;
estilo.set_property("border-radius", "8px")?;
estilo.set_property("margin", "10px")?;
// Adicionar ao body
let body = document.body().unwrap();
body.append_child(&div)?;
// Buscar elementos existentes
if let Some(titulo) = document.get_element_by_id("titulo") {
titulo.set_text_content(Some("Título atualizado pelo Rust!"));
}
// Query selector
let paragrafos = document.query_selector_all("p")?;
console::log_1(&format!("Encontrados {} parágrafos", paragrafos.length()).into());
Ok(())
}
Event Listeners com Closures
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{Document, Event, HtmlInputElement, MouseEvent};
#[wasm_bindgen]
pub fn configurar_eventos() -> Result<(), JsValue> {
let document = web_sys::window().unwrap().document().unwrap();
// Evento de clique
let botao = document.get_element_by_id("meu-botao").unwrap();
let callback = Closure::wrap(Box::new(move |evento: MouseEvent| {
let x = evento.client_x();
let y = evento.client_y();
web_sys::console::log_1(
&format!("Clique em ({}, {})", x, y).into(),
);
}) as Box<dyn FnMut(MouseEvent)>);
botao.add_event_listener_with_callback(
"click",
callback.as_ref().unchecked_ref(),
)?;
callback.forget(); // Manter o closure vivo
// Evento de input
let input = document.get_element_by_id("meu-input").unwrap();
let output = document.get_element_by_id("output").unwrap();
let callback_input = Closure::wrap(Box::new(move |evento: Event| {
let target = evento.target().unwrap();
let input = target.dyn_ref::<HtmlInputElement>().unwrap();
let valor = input.value();
let maiusculo = valor.to_uppercase();
if let Some(el) = web_sys::window()
.unwrap()
.document()
.unwrap()
.get_element_by_id("output")
{
el.set_text_content(Some(&maiusculo));
}
}) as Box<dyn FnMut(Event)>);
input.add_event_listener_with_callback(
"input",
callback_input.as_ref().unchecked_ref(),
)?;
callback_input.forget();
Ok(())
}
js-sys: Tipos JavaScript Nativos
use wasm_bindgen::prelude::*;
use js_sys::{Array, Date, JSON, Map, Object, Promise, Reflect};
#[wasm_bindgen]
pub fn demonstrar_js_sys() -> Result<JsValue, JsValue> {
// Date
let agora = Date::new_0();
web_sys::console::log_1(&format!(
"Data JS: {}/{}/{}",
agora.get_date(),
agora.get_month() + 1,
agora.get_full_year()
).into());
// Array
let arr = Array::new();
arr.push(&JsValue::from("Rust"));
arr.push(&JsValue::from("WebAssembly"));
arr.push(&JsValue::from("JavaScript"));
web_sys::console::log_1(&format!("Array length: {}", arr.length()).into());
// Map
let mapa = Map::new();
mapa.set(&JsValue::from("linguagem"), &JsValue::from("Rust"));
mapa.set(&JsValue::from("versao"), &JsValue::from("1.75"));
// Object
let obj = Object::new();
Reflect::set(&obj, &"nome".into(), &"wasm-bindgen".into())?;
Reflect::set(&obj, &"versao".into(), &"0.2".into())?;
// JSON
let json_str = JSON::stringify(&obj)?;
web_sys::console::log_1(&json_str);
Ok(obj.into())
}
Processamento de Dados Pesado
Um dos melhores casos de uso para WASM: processamento intensivo de dados:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn processar_imagem(dados: &[u8], largura: u32, altura: u32) -> Vec<u8> {
let mut resultado = dados.to_vec();
// Converter para escala de cinza
for pixel in resultado.chunks_exact_mut(4) {
let r = pixel[0] as f32;
let g = pixel[1] as f32;
let b = pixel[2] as f32;
let cinza = (0.299 * r + 0.587 * g + 0.114 * b) as u8;
pixel[0] = cinza;
pixel[1] = cinza;
pixel[2] = cinza;
// pixel[3] (alpha) permanece inalterado
}
resultado
}
#[wasm_bindgen]
pub fn aplicar_blur(dados: &[u8], largura: u32, altura: u32, raio: u32) -> Vec<u8> {
let mut resultado = dados.to_vec();
let w = largura as usize;
let h = altura as usize;
let r = raio as usize;
for y in r..h - r {
for x in r..w - r {
let mut soma_r = 0u32;
let mut soma_g = 0u32;
let mut soma_b = 0u32;
let mut contagem = 0u32;
for dy in 0..=2 * r {
for dx in 0..=2 * r {
let ny = y + dy - r;
let nx = x + dx - r;
let idx = (ny * w + nx) * 4;
soma_r += dados[idx] as u32;
soma_g += dados[idx + 1] as u32;
soma_b += dados[idx + 2] as u32;
contagem += 1;
}
}
let idx = (y * w + x) * 4;
resultado[idx] = (soma_r / contagem) as u8;
resultado[idx + 1] = (soma_g / contagem) as u8;
resultado[idx + 2] = (soma_b / contagem) as u8;
}
}
resultado
}
// Sorting eficiente em WASM
#[wasm_bindgen]
pub fn ordenar_numeros(mut numeros: Vec<f64>) -> Vec<f64> {
numeros.sort_by(|a, b| a.partial_cmp(b).unwrap());
numeros
}
// Busca em texto (muito mais rápido que JS para textos grandes)
#[wasm_bindgen]
pub fn contar_ocorrencias(texto: &str, padrao: &str) -> u32 {
texto.matches(padrao).count() as u32
}
Workflow com wasm-pack
# Build para web (ES modules)
wasm-pack build --target web
# Build para bundlers (webpack, vite, etc.)
wasm-pack build --target bundler
# Build para Node.js
wasm-pack build --target nodejs
# Build com otimizações
wasm-pack build --release --target web
# Testar no navegador
wasm-pack test --chrome --headless
# Publicar no npm
wasm-pack publish
Estrutura do pkg/ gerado:
pkg/
├── meu_projeto_wasm_bg.wasm # Binário WASM
├── meu_projeto_wasm_bg.wasm.d.ts # Tipos do WASM
├── meu_projeto_wasm.js # Glue code JS
├── meu_projeto_wasm.d.ts # Tipos TypeScript
├── package.json # Para npm
└── README.md
Boas Práticas
1. Minimize as Chamadas entre JS e WASM
use wasm_bindgen::prelude::*;
// RUIM: muitas chamadas individuais JS -> WASM
#[wasm_bindgen]
pub fn somar_um(n: f64) -> f64 {
n + 1.0
}
// JS: for (let i = 0; i < 1000; i++) { arr[i] = somar_um(arr[i]); }
// BOM: processar em lote
#[wasm_bindgen]
pub fn somar_um_lote(dados: &[f64]) -> Vec<f64> {
dados.iter().map(|n| n + 1.0).collect()
}
// JS: arr = somar_um_lote(arr);
2. Gerencie Memória Corretamente
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct RecursoPesado {
dados: Vec<u8>,
}
#[wasm_bindgen]
impl RecursoPesado {
#[wasm_bindgen(constructor)]
pub fn novo(tamanho: usize) -> RecursoPesado {
RecursoPesado {
dados: vec![0; tamanho],
}
}
// Método free() é gerado automaticamente pelo wasm-bindgen
// JS deve chamar recurso.free() quando não precisar mais
}
// Uso correto em JS:
const recurso = new RecursoPesado(1024 * 1024);
// ... usar recurso ...
recurso.free(); // IMPORTANTE: liberar memória WASM
3. Use #[wasm_bindgen(start)] para Inicialização
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn inicializar() {
// Configurar panic hook para mensagens de erro úteis
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
web_sys::console::log_1(&"Módulo WASM inicializado!".into());
}
4. Otimize o Tamanho do WASM
# Cargo.toml
[profile.release]
opt-level = "s" # Otimizar para tamanho (ou "z" para menor possível)
lto = true # Link-Time Optimization
codegen-units = 1 # Melhor otimização
strip = true # Remover símbolos de debug
[dependencies]
console_error_panic_hook = { version = "0.1", optional = true }
wee_alloc = { version = "0.4", optional = true }
[features]
default = ["console_error_panic_hook"]
# Usar wasm-opt para otimização adicional (instalado com wasm-pack)
wasm-pack build --release
5. Tratamento de Erros Informativo
use wasm_bindgen::prelude::*;
// Configurar panic hook para erros legíveis no console do navegador
pub fn configurar_panic_hook() {
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
}
#[wasm_bindgen]
pub fn operacao_que_pode_falhar(dados: &str) -> Result<String, JsValue> {
configurar_panic_hook();
serde_json::from_str::<serde_json::Value>(dados)
.map(|v| v.to_string())
.map_err(|e| JsValue::from_str(&format!("Erro de parsing: {}", e)))
}
Exemplos Práticos
Exemplo Completo: Componente Web Interativo
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{
CanvasRenderingContext2d, Document, HtmlCanvasElement,
HtmlElement, MouseEvent, Window,
};
use std::cell::RefCell;
use std::rc::Rc;
fn window() -> Window {
web_sys::window().unwrap()
}
fn document() -> Document {
window().document().unwrap()
}
#[wasm_bindgen]
pub struct App {
canvas: HtmlCanvasElement,
ctx: CanvasRenderingContext2d,
}
#[derive(Clone)]
struct Ponto {
x: f64,
y: f64,
cor: String,
raio: f64,
}
#[wasm_bindgen]
impl App {
#[wasm_bindgen(constructor)]
pub fn novo(canvas_id: &str) -> Result<App, JsValue> {
let document = document();
let canvas = document
.get_element_by_id(canvas_id)
.ok_or_else(|| JsValue::from_str("Canvas não encontrado"))?
.dyn_into::<HtmlCanvasElement>()?;
let ctx = canvas
.get_context("2d")?
.unwrap()
.dyn_into::<CanvasRenderingContext2d>()?;
Ok(App { canvas, ctx })
}
pub fn iniciar(&self) -> Result<(), JsValue> {
let largura = self.canvas.width() as f64;
let altura = self.canvas.height() as f64;
// Fundo
self.ctx.set_fill_style_str("#1a1a2e");
self.ctx.fill_rect(0.0, 0.0, largura, altura);
// Título
self.ctx.set_fill_style_str("#e94560");
self.ctx.set_font("24px Arial");
self.ctx.set_text_align("center");
self.ctx
.fill_text("Clique para desenhar!", largura / 2.0, 40.0)?;
// Configurar evento de clique
let canvas = self.canvas.clone();
let ctx = self
.canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<CanvasRenderingContext2d>()
.unwrap();
let pontos = Rc::new(RefCell::new(Vec::<Ponto>::new()));
let pontos_clone = pontos.clone();
let callback = Closure::wrap(Box::new(move |evento: MouseEvent| {
let rect = canvas.get_bounding_client_rect();
let x = evento.client_x() as f64 - rect.left();
let y = evento.client_y() as f64 - rect.top();
let cores = ["#e94560", "#0f3460", "#16213e", "#533483", "#e94560"];
let idx = pontos_clone.borrow().len() % cores.len();
let ponto = Ponto {
x,
y,
cor: cores[idx].to_string(),
raio: 5.0 + (pontos_clone.borrow().len() as f64 % 20.0),
};
// Desenhar círculo
ctx.begin_path();
ctx.arc(ponto.x, ponto.y, ponto.raio, 0.0, std::f64::consts::PI * 2.0)
.unwrap();
ctx.set_fill_style_str(&ponto.cor);
ctx.fill();
// Conectar ao ponto anterior
let pontos_ref = pontos_clone.borrow();
if let Some(anterior) = pontos_ref.last() {
ctx.begin_path();
ctx.move_to(anterior.x, anterior.y);
ctx.line_to(ponto.x, ponto.y);
ctx.set_stroke_style_str(&ponto.cor);
ctx.set_line_width(2.0);
ctx.stroke();
}
drop(pontos_ref);
pontos_clone.borrow_mut().push(ponto);
}) as Box<dyn FnMut(MouseEvent)>);
self.canvas.add_event_listener_with_callback(
"click",
callback.as_ref().unchecked_ref(),
)?;
callback.forget();
Ok(())
}
pub fn limpar(&self) {
let largura = self.canvas.width() as f64;
let altura = self.canvas.height() as f64;
self.ctx.set_fill_style_str("#1a1a2e");
self.ctx.fill_rect(0.0, 0.0, largura, altura);
}
pub fn desenhar_grafico(&self, dados: &[f64]) -> Result<(), JsValue> {
let largura = self.canvas.width() as f64;
let altura = self.canvas.height() as f64;
let margem = 50.0;
// Limpar
self.ctx.set_fill_style_str("#1a1a2e");
self.ctx.fill_rect(0.0, 0.0, largura, altura);
if dados.is_empty() {
return Ok(());
}
let max_val = dados.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
let min_val = dados.iter().cloned().fold(f64::INFINITY, f64::min);
let range = if max_val == min_val { 1.0 } else { max_val - min_val };
let area_largura = largura - 2.0 * margem;
let area_altura = altura - 2.0 * margem;
let passo = area_largura / (dados.len() - 1).max(1) as f64;
// Eixos
self.ctx.set_stroke_style_str("#555");
self.ctx.set_line_width(1.0);
self.ctx.begin_path();
self.ctx.move_to(margem, margem);
self.ctx.line_to(margem, altura - margem);
self.ctx.line_to(largura - margem, altura - margem);
self.ctx.stroke();
// Linha do gráfico
self.ctx.set_stroke_style_str("#e94560");
self.ctx.set_line_width(2.0);
self.ctx.begin_path();
for (i, &valor) in dados.iter().enumerate() {
let x = margem + i as f64 * passo;
let y = altura - margem - ((valor - min_val) / range * area_altura);
if i == 0 {
self.ctx.move_to(x, y);
} else {
self.ctx.line_to(x, y);
}
}
self.ctx.stroke();
// Pontos
self.ctx.set_fill_style_str("#e94560");
for (i, &valor) in dados.iter().enumerate() {
let x = margem + i as f64 * passo;
let y = altura - margem - ((valor - min_val) / range * area_altura);
self.ctx.begin_path();
self.ctx.arc(x, y, 4.0, 0.0, std::f64::consts::PI * 2.0)?;
self.ctx.fill();
}
// Labels
self.ctx.set_fill_style_str("#aaa");
self.ctx.set_font("12px monospace");
self.ctx.set_text_align("right");
self.ctx.fill_text(&format!("{:.1}", max_val), margem - 5.0, margem + 5.0)?;
self.ctx.fill_text(
&format!("{:.1}", min_val),
margem - 5.0,
altura - margem + 5.0,
)?;
Ok(())
}
}
HTML para o exemplo:
<!DOCTYPE html>
<html>
<head>
<title>Rust WASM Canvas</title>
<style>
body {
background: #0f0f23;
color: white;
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
canvas {
border: 2px solid #333;
border-radius: 8px;
cursor: crosshair;
}
button {
margin: 10px;
padding: 10px 20px;
background: #e94560;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover { background: #c73e55; }
</style>
</head>
<body>
<h1>Rust + WebAssembly Canvas</h1>
<canvas id="canvas" width="800" height="500"></canvas>
<div>
<button id="btn-limpar">Limpar</button>
<button id="btn-grafico">Gráfico Demo</button>
</div>
<script type="module">
import init, { App } from './pkg/meu_projeto_wasm.js';
async function main() {
await init();
const app = new App("canvas");
app.iniciar();
document.getElementById("btn-limpar").onclick = () => {
app.limpar();
app.iniciar();
};
document.getElementById("btn-grafico").onclick = () => {
const dados = Array.from({length: 20}, () => Math.random() * 100);
app.desenhar_grafico(new Float64Array(dados));
};
}
main();
</script>
</body>
</html>
Comparação com Alternativas
| Tecnologia | Caso de uso | Destaques |
|---|---|---|
wasm-bindgen | Interop Rust-JS | Mais maduro, tipagem rica |
stdweb | Interop Rust-JS | Descontinuado, use wasm-bindgen |
Yew | Framework SPA | Similar ao React, componentes |
Leptos | Framework SPA | Signals, SSR, muito performático |
Dioxus | Framework multiplataforma | Web, desktop, mobile |
Trunk | Build tool | Bundling para projetos WASM |
Conclusão
A combinação de wasm-bindgen, web-sys e wasm-pack torna Rust uma opção de primeira classe para WebAssembly. Desde processamento de imagens e criptografia até jogos e visualizações de dados, o WASM com Rust entrega performance nativa no navegador com segurança de memória.
O workflow moderno com wasm-pack simplifica build, testes e publicação, gerando automaticamente tipos TypeScript e pacotes npm. A interoperabilidade bidirecional entre Rust e JavaScript, com conversão automática de tipos, torna o desenvolvimento surpreendentemente ergonômico.
Próximos passos:
- Explore frameworks como Yew ou Leptos para SPAs completas em Rust
- Veja criterion para benchmarkar seu código WASM
- Confira serde para serialização de dados entre Rust e JS
- Aprofunde o contexto em Rust e WebAssembly em 2026 e no tutorial de WebAssembly com Rust
- Se quer transformar isso em carreira, veja vagas Rust e empresas que usam Rust no Brasil