Introdução
WebAssembly (Wasm) revolucionou o que é possível fazer no navegador, e Rust é a linguagem que melhor aproveita essa tecnologia. Enquanto C++ e Go também compilam para Wasm, Rust se destaca por produzir binários menores, sem garbage collector (crítico para Wasm, onde cada kilobyte importa) e com uma interoperabilidade excepcional com JavaScript através do wasm-bindgen.
Em 2026, WebAssembly não é mais apenas para demos técnicas. Empresas como Figma, Cloudflare, Google e Adobe usam Wasm em produção para processamento de imagens, editores colaborativos, engines de busca no cliente e muito mais. Neste artigo, vamos explorar o ecossistema Rust para Wasm, construir um projeto prático e entender quando vale a pena substituir JavaScript por Rust+Wasm.
O Ecossistema Rust para WebAssembly
Ferramentas Essenciais
| Ferramenta | Função |
|---|---|
| wasm-pack | Build tool que compila Rust para Wasm e gera bindings JS |
| wasm-bindgen | Ponte entre Rust e JavaScript — permite chamar APIs JS do Rust |
| web-sys | Bindings para todas as Web APIs (DOM, Canvas, Fetch, etc.) |
| js-sys | Bindings para objetos nativos do JavaScript |
| Trunk | Build tool e dev server para aplicações Wasm puras |
| wasm-opt | Otimizador de binários Wasm (parte do Binaryen) |
Frameworks Frontend em Rust
| Framework | Paradigma | Destaque |
|---|---|---|
| Yew | Component-based (semelhante ao React) | Maduro, grande comunidade |
| Leptos | Signals (semelhante ao SolidJS) | SSR nativo, fine-grained reactivity |
| Dioxus | Semelhante ao React com suporte multi-plataforma | Desktop, mobile, web |
| Sycamore | Signals, sem Virtual DOM | Performance máxima |
Configuração Base (Cargo.toml)
[package]
name = "meu-projeto-wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = [
"Document", "Element", "HtmlElement",
"Window", "console", "HtmlCanvasElement",
"CanvasRenderingContext2d",
] }
js-sys = "0.3"
serde = { version = "1", features = ["derive"] }
serde-wasm-bindgen = "0.6"
[profile.release]
opt-level = "z" # Otimizar para tamanho
lto = true # Link-Time Optimization
codegen-units = 1 # Melhor otimização
strip = true # Remover símbolos de debug
Exemplo Prático: Processador de Imagens no Navegador
Vamos construir um módulo Wasm que aplica filtros em imagens diretamente no navegador, demonstrando a performance superior de Rust para operações computacionais intensivas.
Código Rust (src/lib.rs)
use wasm_bindgen::prelude::*;
use web_sys::console;
/// Aplica um filtro de escala de cinza nos pixels da imagem.
/// Recebe os dados de pixel como um slice mutável (RGBA).
#[wasm_bindgen]
pub fn filtro_escala_cinza(pixels: &mut [u8]) {
for chunk in pixels.chunks_exact_mut(4) {
let r = chunk[0] as f32;
let g = chunk[1] as f32;
let b = chunk[2] as f32;
// Fórmula de luminância perceptual (ITU-R BT.709)
let cinza = (0.2126 * r + 0.7152 * g + 0.0722 * b) as u8;
chunk[0] = cinza;
chunk[1] = cinza;
chunk[2] = cinza;
// chunk[3] (alpha) permanece inalterado
}
}
/// Aplica um filtro de desfoque (blur) simples usando média de vizinhos.
#[wasm_bindgen]
pub fn filtro_blur(pixels: &mut [u8], largura: u32, altura: u32, raio: u32) {
let len = pixels.len();
let mut saida = vec![0u8; len];
let largura = largura as usize;
let altura = altura as usize;
let raio = raio as usize;
for y in 0..altura {
for x in 0..largura {
let mut r_total: u64 = 0;
let mut g_total: u64 = 0;
let mut b_total: u64 = 0;
let mut contagem: u64 = 0;
let y_min = y.saturating_sub(raio);
let y_max = (y + raio + 1).min(altura);
let x_min = x.saturating_sub(raio);
let x_max = (x + raio + 1).min(largura);
for ny in y_min..y_max {
for nx in x_min..x_max {
let idx = (ny * largura + nx) * 4;
r_total += pixels[idx] as u64;
g_total += pixels[idx + 1] as u64;
b_total += pixels[idx + 2] as u64;
contagem += 1;
}
}
let idx = (y * largura + x) * 4;
saida[idx] = (r_total / contagem) as u8;
saida[idx + 1] = (g_total / contagem) as u8;
saida[idx + 2] = (b_total / contagem) as u8;
saida[idx + 3] = pixels[idx + 3]; // preserva alpha
}
}
pixels.copy_from_slice(&saida);
}
/// Ajusta o brilho da imagem. Valores positivos clareiam, negativos escurecem.
#[wasm_bindgen]
pub fn ajustar_brilho(pixels: &mut [u8], fator: f32) {
for chunk in pixels.chunks_exact_mut(4) {
chunk[0] = ((chunk[0] as f32 * fator).clamp(0.0, 255.0)) as u8;
chunk[1] = ((chunk[1] as f32 * fator).clamp(0.0, 255.0)) as u8;
chunk[2] = ((chunk[2] as f32 * fator).clamp(0.0, 255.0)) as u8;
}
}
/// Aplica um filtro sépia (tom envelhecido).
#[wasm_bindgen]
pub fn filtro_sepia(pixels: &mut [u8]) {
for chunk in pixels.chunks_exact_mut(4) {
let r = chunk[0] as f32;
let g = chunk[1] as f32;
let b = chunk[2] as f32;
chunk[0] = ((r * 0.393 + g * 0.769 + b * 0.189).min(255.0)) as u8;
chunk[1] = ((r * 0.349 + g * 0.686 + b * 0.168).min(255.0)) as u8;
chunk[2] = ((r * 0.272 + g * 0.534 + b * 0.131).min(255.0)) as u8;
}
}
/// Retorna informações sobre o módulo Wasm.
#[wasm_bindgen]
pub fn info() -> String {
format!(
"Processador de Imagens Wasm v{} — compilado com Rust",
env!("CARGO_PKG_VERSION")
)
}
#[wasm_bindgen(start)]
pub fn inicializar() {
console::log_1(&"Módulo Wasm de processamento de imagens carregado!".into());
}
Integração com JavaScript
import init, {
filtro_escala_cinza,
filtro_blur,
ajustar_brilho,
filtro_sepia,
info
} from './pkg/meu_projeto_wasm.js';
async function main() {
await init();
console.log(info());
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const img = document.getElementById('imagem-fonte');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// Benchmark: Rust (Wasm) vs JavaScript puro
console.time('Wasm - Escala de cinza');
filtro_escala_cinza(imageData.data);
console.timeEnd('Wasm - Escala de cinza');
ctx.putImageData(imageData, 0, 0);
}
main();
Build e Execução
# Instalar wasm-pack
cargo install wasm-pack
# Compilar para navegador
wasm-pack build --target web --release
# O resultado estará em ./pkg/
# - meu_projeto_wasm_bg.wasm (binário otimizado)
# - meu_projeto_wasm.js (bindings JavaScript)
# - meu_projeto_wasm.d.ts (tipos TypeScript)
Aplicação Full-Stack com Yew
Para quem quer construir uma SPA completa em Rust, Yew oferece uma experiência semelhante ao React:
[package]
name = "meu-app-yew"
version = "0.1.0"
edition = "2021"
[dependencies]
yew = { version = "0.21", features = ["csr"] }
gloo-net = "0.6"
serde = { version = "1", features = ["derive"] }
wasm-bindgen-futures = "0.4"
use gloo_net::http::Request;
use serde::Deserialize;
use yew::prelude::*;
#[derive(Clone, Debug, Deserialize, PartialEq)]
struct Repositorio {
name: String,
description: Option<String>,
stargazers_count: u32,
html_url: String,
}
#[function_component(App)]
fn app() -> Html {
let repos = use_state(|| Vec::<Repositorio>::new());
let carregando = use_state(|| true);
{
let repos = repos.clone();
let carregando = carregando.clone();
use_effect_with((), move |_| {
wasm_bindgen_futures::spawn_local(async move {
let resposta: Vec<Repositorio> = Request::get(
"https://api.github.com/users/nickel-org/repos?sort=stars&per_page=5"
)
.send()
.await
.unwrap()
.json()
.await
.unwrap();
repos.set(resposta);
carregando.set(false);
});
});
}
html! {
<div class="app">
<h1>{"Repositórios Rust Populares"}</h1>
if *carregando {
<p>{"Carregando..."}</p>
} else {
<ul>
{ for repos.iter().map(|repo| html! {
<li key={repo.name.clone()}>
<a href={repo.html_url.clone()} target="_blank">
<strong>{ &repo.name }</strong>
</a>
{" — "}
{ repo.description.as_deref().unwrap_or("Sem descrição") }
<span class="stars">
{ format!(" ★ {}", repo.stargazers_count) }
</span>
</li>
})}
</ul>
}
</div>
}
}
fn main() {
yew::Renderer::<App>::new().render();
}
Build com Trunk
# Instalar Trunk
cargo install trunk
# Criar index.html na raiz do projeto
# Rodar servidor de desenvolvimento
trunk serve
# Build para produção
trunk build --release
Performance: Rust+Wasm vs JavaScript
Em operações computacionais intensivas, Rust compilado para Wasm costuma ser 2-10x mais rápido que JavaScript equivalente:
| Operação | JavaScript | Rust (Wasm) | Speedup |
|---|---|---|---|
| Filtro de imagem (1920x1080) | 120ms | 15ms | 8x |
| Parsing JSON grande (10MB) | 250ms | 80ms | 3x |
| Cálculo de hash SHA-256 | 45ms | 8ms | 5.6x |
| Simulação física (1000 objetos) | 16ms/frame | 3ms/frame | 5.3x |
| Compressão de texto (1MB) | 200ms | 30ms | 6.7x |
A vantagem diminui para operações simples de DOM ou I/O, onde o overhead de atravessar a ponte Wasm/JS anula os ganhos. A regra geral: use Wasm para computação pesada, mantenha JS para manipulação de DOM e I/O.
Empresas Usando Rust + WebAssembly
- Figma: Motor de renderização do editor de design é Rust compilado para Wasm, processando milhões de operações gráficas no navegador
- Cloudflare Workers: Runtime serverless que executa Wasm no edge, com suporte de primeira classe para Rust
- Google: O Google Earth usa Wasm para renderização 3D no navegador
- Adobe: Photoshop para web utiliza Wasm para filtros e processamento de imagem
- Amazon Prime Video: Componentes de streaming e decodificação
- 1Password: Lógica de criptografia no navegador via Wasm
Como Começar
- Instale as ferramentas: Certifique-se de ter o Rust instalado (guia de instalação) e adicione o target wasm:
rustup target add wasm32-unknown-unknown - Aprenda o básico de Rust: Tutorial de primeiros passos
- Siga o tutorial de WebAssembly: Tutorial completo de WebAssembly com Rust
- Entenda serialização: Receita de JSON — essencial para trocar dados com JS
- Explore frameworks: Comece com Yew ou Leptos para uma SPA completa
Conclusão
Rust e WebAssembly formam uma combinação poderosa que está redefinindo o que é possível no navegador. Para tarefas computacionais intensivas — processamento de imagem, criptografia, simulações, parsers — a performance de Rust compilado para Wasm é imbatível. Com ferramentas como wasm-pack e frameworks como Yew e Leptos, a experiência de desenvolvimento melhorou drasticamente, tornando viável escrever aplicações web completas em Rust.
Veja Também
- Tutorial: WebAssembly com Rust — Tutorial passo a passo para começar
- Rust para Desenvolvimento Web — Backend e full-stack com Rust
- Rust para Jogos — Wasm também é usado para jogos no navegador
- Receita: Parse JSON — Trocar dados entre Rust e JavaScript
- Receita: Medir Tempo de Execução — Compare performance Wasm vs JS
- Instalação do Rust — Configure seu ambiente de desenvolvimento