Back

Rust para Desenvolvedores JavaScript: Guia Prático para Suas Primeiras 1000 Linhas (2026)

Você ouve isso em todo lugar: "Rust é o futuro." O Stack Overflow chama de linguagem mais admirada pelo 8º ano consecutivo. O Discord reconstruiu sua infraestrutura nela. A Cloudflare roda serviços críticos de edge com ela. O engine multiplayer do Figma? Rust. Até a Microsoft está reescrevendo componentes core do Windows em Rust.

Mas tem um problema—se você é desenvolvedor JavaScript ou TypeScript, todo tutorial de Rust parece ter sido escrito pra alguém com background em C++. Ownership? Borrowing? Lifetimes? A terminologia sozinha já é suficiente pra você fechar a aba e voltar pro seu confortável npm install.

Este guia é diferente. Vamos aprender Rust através da perspectiva de um desenvolvedor JavaScript. No final, você terá escrito suas primeiras 1000 linhas de Rust e realmente entenderá o que está acontecendo por baixo dos panos.

Por que um dev web ia querer aprender Rust?

Antes de codar, bora ser direto: Por que quem faz web ia aprender uma linguagem de sistemas?

A Realidade de Performance

JavaScript é interpretado (ou compilado JIT). Rust compila pra código de máquina nativo. A diferença não é sutil:

// JavaScript: Parsear JSON, encontrar valor máximo const data = JSON.parse(hugeJsonString); const max = Math.max(...data.numbers); // Tempo de execução: ~450ms para 10 milhões de números
// Rust: Mesma operação let data: Data = serde_json::from_str(&huge_json_string)?; let max = data.numbers.iter().max(); // Tempo de execução: ~12ms para 10 milhões de números

Não é erro de digitação—37x mais rápido para a mesma operação lógica. E isso importa quando você está:

  • Construindo ferramentas CLI que precisam parecer instantâneas
  • Processando arquivos grandes (ferramentas de build, linters)
  • Escrevendo funções serverless onde tempo de cold start = dinheiro
  • Criando módulos WebAssembly para tarefas pesadas no navegador

A Ponte WebAssembly

Aqui é onde fica interessante pra desenvolvedores web. Rust compila pra WebAssembly (WASM) melhor que qualquer outra linguagem:

// Este código Rust... #[wasm_bindgen] pub fn fibonacci(n: u32) -> u32 { match n { 0 => 0, 1 => 1, _ => fibonacci(n - 1) + fibonacci(n - 2) } }

...vira um arquivo .wasm que você pode importar diretamente no JavaScript:

import init, { fibonacci } from './pkg/my_rust_lib.js'; await init(); console.log(fibonacci(40)); // Roda 10-20x mais rápido que JS puro

Empresas como Figma, Photoshop (versão web), e Google Earth usam exatamente esse padrão pra código crítico de performance.

Seu Primeiro Programa Rust: Comparando com JavaScript

Vamos começar com algo familiar. Aqui está um programa simples nas duas linguagens:

JavaScript:

function greet(name) { const message = `Hello, ${name}!`; console.log(message); } greet("World");

Rust:

fn greet(name: &str) { let message = format!("Hello, {}!", name); println!("{}", message); } fn main() { greet("World"); }

Já dá pra ver algumas diferenças:

  • fn ao invés de function
  • Tipos são explícitos: name: &str
  • let funciona similar, mas const não existe da mesma forma
  • format! e println! têm ! porque são macros
  • Todo programa Rust precisa de uma função main

Configurando Seu Ambiente

Antes de continuar, vamos instalar Rust:

# Instalar Rust (funciona no macOS, Linux, WSL) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Verificar instalação rustc --version cargo --version

cargo é o gerenciador de pacotes e ferramenta de build do Rust—pense nele como npm + webpack combinados.

# Criar novo projeto (tipo npm init) cargo new my-first-rust-app cd my-first-rust-app # Rodar o projeto cargo run

Os 3 conceitos principais: Ownership, Borrowing e Lifetimes

É aqui que devs JS costumam travar. Mas relaxa, vou explicar com analogias que você vai entender.

Problema: A Gestão de Memória Escondida do JavaScript

Em JavaScript, você nunca pensa em memória:

function processData() { const data = [1, 2, 3, 4, 5]; // Memória alocada const doubled = data.map(x => x * 2); // Mais memória alocada return doubled; } // Memória... eventualmente coletada pelo GC // Você não tem controle sobre quando a memória é liberada // Isso pode causar pausas inesperadas de GC em código crítico

JavaScript usa garbage collection. É conveniente mas imprevisível. Rust te dá controle sem os perigos do gerenciamento manual de memória.

Ownership: Um Dono, Sempre

Em Rust, todo valor tem exatamente um dono:

fn main() { let s1 = String::from("hello"); // s1 é dono da string let s2 = s1; // Ownership MOVE pra s2 // println!("{}", s1); // ERRO! s1 não é mais dono de nada println!("{}", s2); // Funciona tranquilo }

Em termos de JavaScript, imagina se isso acontecesse:

// "Ownership" hipotético em JavaScript let s1 = "hello"; let s2 = s1; // Em Rust, isso invalidaria s1 console.log(s1); // Em Rust, isso seria um erro!

Por que Rust faz isso? Porque quando s2 sai do escopo, Rust sabe exatamente quando liberar a memória. Não precisa de garbage collector.

Borrowing: Referências Sem Ownership

Mas espera—e se você só quer usar um valor sem tomar ownership? Isso é borrowing:

fn calculate_length(s: &String) -> usize { s.len() } // s sai do escopo, mas não é dono da String, então nada acontece fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); // EMPRESTAMOS s1 com & println!("O tamanho de '{}' é {}.", s1, len); // s1 ainda válido! }

Pense no & como dizer "só tô olhando, não tô levando."

Comparando com JavaScript:

// JavaScript passa objetos por referência de qualquer jeito function calculateLength(s) { return s.length; } const s1 = "hello"; const len = calculateLength(s1); console.log(`O tamanho de '${s1}' é ${len}.`); // Funciona igual

A diferença? Em Rust, o compilador garante que calculate_length não pode modificar ou manter s1. Em JavaScript, você só tem que confiar na função.

Borrowing Mutável: A Regra do Escritor Único

Em Rust, você pode ter:

  • Várias referências imutáveis (&T)
  • OU uma referência mutável (&mut T)

Nunca as duas ao mesmo tempo.

fn main() { let mut s = String::from("hello"); let r1 = &s; // OK: borrow imutável let r2 = &s; // OK: outro borrow imutável // let r3 = &mut s; // ERRO! Não pode fazer borrow mutável enquanto tem borrow imutável println!("{} e {}", r1, r2); // r1 e r2 não são mais usados depois desse ponto let r3 = &mut s; // Agora funciona! r3.push_str(", world"); }

Por que isso importa: Essa regra previne data races em tempo de compilação. Em JavaScript, você provavelmente já encontrou bugs onde uma parte do seu código muta um objeto enquanto outra parte está lendo. Rust torna isso impossível.

Padrões do dia a dia: JS vs Rust

Bora traduzir código que você usa todo dia.

Arrays e Iteração

JavaScript:

const numbers = [1, 2, 3, 4, 5]; // Map const doubled = numbers.map(x => x * 2); // Filter const evens = numbers.filter(x => x % 2 === 0); // Reduce const sum = numbers.reduce((acc, x) => acc + x, 0); // Find const firstEven = numbers.find(x => x % 2 === 0);

Rust:

let numbers = vec![1, 2, 3, 4, 5]; // Map let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect(); // Filter let evens: Vec<&i32> = numbers.iter().filter(|x| *x % 2 == 0).collect(); // Reduce (chamado fold em Rust) let sum: i32 = numbers.iter().fold(0, |acc, x| acc + x); // Ou simplesmente: let sum: i32 = numbers.iter().sum(); // Find let first_even = numbers.iter().find(|x| *x % 2 == 0);

Diferenças principais:

  • |x| é a sintaxe de closure do Rust (tipo arrow functions)
  • .iter() cria um iterador
  • .collect() transforma um iterador de volta em uma coleção
  • Você precisa especificar tipos ou deixar Rust inferir

Valores Opcionais (tratando null)

JavaScript:

function findUser(id) { const user = database.get(id); if (user === null || user === undefined) { return "Anonymous"; } return user.name; } // Ou com optional chaining const name = user?.name ?? "Anonymous";

Rust:

fn find_user(id: u32) -> String { let user = database.get(id); match user { Some(u) => u.name.clone(), None => String::from("Anonymous"), } } // Ou mais conciso let name = user.map(|u| u.name.clone()).unwrap_or(String::from("Anonymous")); // Ou ainda mais simples com if let if let Some(user) = database.get(id) { println!("Encontrado: {}", user.name); }

Rust não tem null. Em vez disso, você usa Option<T>:

  • Some(value) = tem um valor
  • None = não tem valor

O compilador te obriga a tratar os dois casos. Chega de erros de "undefined is not an object"!

Tratamento de Erros

JavaScript:

async function fetchData(url) { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error: ${response.status}`); } return await response.json(); } catch (error) { console.error("Fetch falhou:", error); throw error; } }

Rust:

use reqwest; async fn fetch_data(url: &str) -> Result<serde_json::Value, reqwest::Error> { let response = reqwest::get(url).await?; let data = response.json().await?; Ok(data) } // Usando match fetch_data("https://api.example.com/data").await { Ok(data) => println!("Dados recebidos: {:?}", data), Err(e) => eprintln!("Fetch falhou: {}", e), }

O operador ? é o equivalente do Rust a "se isso falhar, retorna o erro imediatamente." É tipo propagação automática de try-catch.

Bora construir algo de verdade: Uma CLI pra JSON

Vamos fazer uma ferramenta CLI que um dev JS usaria—um formatador de JSON:

use std::env; use std::fs; use serde_json::{Value, to_string_pretty}; fn main() { // Pegar argumentos de linha de comando (tipo process.argv) let args: Vec<String> = env::args().collect(); if args.len() != 2 { eprintln!("Uso: {} <arquivo.json>", args[0]); std::process::exit(1); } let filename = &args[1]; // Ler arquivo (tipo fs.readFileSync) let contents = match fs::read_to_string(filename) { Ok(c) => c, Err(e) => { eprintln!("Erro lendo arquivo: {}", e); std::process::exit(1); } }; // Parsear JSON let parsed: Value = match serde_json::from_str(&contents) { Ok(v) => v, Err(e) => { eprintln!("JSON inválido: {}", e); std::process::exit(1); } }; // Pretty print match to_string_pretty(&parsed) { Ok(pretty) => println!("{}", pretty), Err(e) => eprintln!("Erro formatando: {}", e), }; }

Pra rodar:

# Adicionar dependência ao Cargo.toml # [dependencies] # serde_json = "1.0" cargo build --release ./target/release/json-formatter bagunçado.json

O binário compilado tem ~1MB e roda em milissegundos—compara com enviar Node.js junto com sua ferramenta CLI.

Rust Async: Não É Tão Diferente

JavaScript moderno é tudo sobre async/await. Rust também tem:

JavaScript:

async function fetchMultiple(urls) { const promises = urls.map(url => fetch(url).then(r => r.json())); const results = await Promise.all(promises); return results; }

Rust (com runtime tokio):

use futures::future::join_all; async fn fetch_multiple(urls: Vec<&str>) -> Vec<Result<String, reqwest::Error>> { let futures = urls.iter().map(|url| async move { let response = reqwest::get(*url).await?; response.text().await }); join_all(futures).await } #[tokio::main] async fn main() { let urls = vec![ "https://api.example.com/1", "https://api.example.com/2", ]; let results = fetch_multiple(urls).await; for result in results { match result { Ok(body) => println!("Recebido: {:.100}...", body), Err(e) => eprintln!("Erro: {}", e), } } }

A estrutura é impressionantemente similar! A diferença principal é que Rust precisa de um runtime async explícito (tokio é o mais popular).

O Ecossistema Rust pra Desenvolvedores Web

Aqui estão os crates (pacotes npm do Rust) que você mais vai usar:

Frameworks Web

  • Axum - O novo padrão, criado pelo time do Tokio
  • Actix Web - Testado em batalha, extremamente rápido
  • Rocket - Amigável pro dev, ótima ergonomia

Serialização

  • Serde - O padrão de facto pra JSON, YAML, TOML, etc.
  • serde_json - JSON especificamente

Cliente HTTP

  • Reqwest - Tipo axios pro Rust

Ferramentas CLI

  • Clap - Parsing de argumentos (tipo commander.js)
  • Indicatif - Barras de progresso
  • Colored - Cores no terminal

WebAssembly

  • wasm-bindgen - Interop JS/Rust
  • wasm-pack - Build e publicação de pacotes WASM

Comparação de Performance: Números Reais

Vamos comparar uma carga de trabalho realista—processar um arquivo JSON de 100MB:

TarefaNode.jsRustMelhoria
Parsear JSON2.3s0.18s12.7x
Encontrar emails (regex)4.1s0.31s13.2x
Transformar & serializar3.8s0.24s15.8x
Uso de memória890MB210MB4.2x menos

Esses números importam quando você está:

  • Construindo ferramentas de build (tipo esbuild, escrito em Go por razões similares)
  • Processando logs ou datasets grandes
  • Rodando em ambientes com memória limitada (serverless, edge)

Erros que todo dev JS comete no começo

1. Strings São Complicadas

let s1 = "hello"; // &str - um slice de string (emprestado) let s2 = String::from("hello"); // String - uma string owned // Você não pode fazer isso: // let s3: String = "hello"; // Erro! // Você precisa converter: let s3: String = "hello".to_string(); let s4: String = String::from("hello");

Regra geral: Use &str pra parâmetros de função, String quando você precisa ser dono dos dados.

2. Sem Exceções, Só Results

// Isso não compila - você deve tratar o Result let file = File::open("data.txt"); // Retorna Result<File, Error> // Você deve tratar let file = File::open("data.txt")?; // Propagar erro // ou let file = File::open("data.txt").unwrap(); // Panic se erro // ou let file = File::open("data.txt").expect("Falhou ao abrir arquivo"); // Panic com mensagem

3. Imutabilidade É o Padrão

let x = 5; // x = 6; // Erro! Variáveis são imutáveis por padrão let mut y = 5; y = 6; // Funciona!

Isso é o oposto do let do JavaScript (mutável) vs const (imutável).

4. Sem Coerção Implícita de Tipos

let x: i32 = 5; let y: i64 = 10; // let z = x + y; // Erro! Não dá pra somar i32 e i64 let z = x as i64 + y; // Deve converter explicitamente

Ok, e agora? O que estudar?

Aqui vai um roadmap realista:

Semana 1-2: Básico

  • Complete os primeiros 8 capítulos do "The Rust Book" (grátis online)
  • Escreva programas pequenos: FizzBuzz, leitor de arquivos, CLI simples

Semana 3-4: Aprofundando em Ownership

  • Releia os capítulos de ownership
  • Complete exercícios do Rustlings (prática interativa)
  • Construa uma app CLI de TODO com persistência em arquivo

Mês 2: Desenvolvimento Web

  • Construa uma REST API com Axum
  • Conecte a um banco de dados (SQLx ou Diesel)
  • Deploy numa plataforma cloud

Mês 3: WebAssembly

  • Construa um módulo WASM
  • Integre com uma app React/Vue/Svelte
  • Compare performance com JavaScript puro

Conclusão: Rust vale a pena ou não?

Pra desenvolvedores JavaScript, Rust não é um substituto—é um complemento. Você ainda vai escrever suas apps web em TypeScript. Mas quando você precisar de:

  • Performance máxima pra tarefas compute-heavy
  • Latência previsível sem pausas de GC
  • Binários pequenos pra CLI tools ou serverless
  • WebAssembly pra performance no navegador

...Rust é a resposta.

A curva de aprendizado é real. Ownership e borrowing vão te confundir no começo. O compilador vai rejeitar seu código constantemente (mas as mensagens de erro são genuinamente úteis).

Mas quando clicar—e vai clicar—você vai ter um superpoder que a maioria dos desenvolvedores JavaScript não tem. Você vai entender como memória realmente funciona. Você vai escrever código mais seguro em qualquer linguagem. E você vai ter uma ferramenta que pode resolver problemas que JavaScript simplesmente não consegue.

Começa com algo pequeno. Um formatador JSON. Um renomeador de arquivos. Uma ferramenta CLI simples. Deixa o compilador te ensinar. E antes que você perceba, você vai estar escrevendo Rust que roda 20x mais rápido que seu JavaScript.

Bem-vindo ao Rust. O compilador é rígido, mas tá do seu lado.

rustjavascripttypescriptsystems-programmingweb-developmentprogramming-languages

Explore ferramentas relacionadas

Experimente estas ferramentas gratuitas do Pockit