---
title: "Rust para Programadores JavaScript: Transição | Rust Brasil"
url: "https://rustlang.com.br/blog/rust-para-programadores-javascript/"
markdown_url: "https://rustlang.com.br/blog/rust-para-programadores-javascript.MD"
description: "Guia de transição de JavaScript/TypeScript para Rust: sintaxe, tipos, async e ferramentas comparados lado a lado."
date: "2026-02-15"
author: "Equipe Rust Brasil"
---

# Rust para Programadores JavaScript: Transição | Rust Brasil

Guia de transição de JavaScript/TypeScript para Rust: sintaxe, tipos, async e ferramentas comparados lado a lado.


Se você é um desenvolvedor JavaScript (ou TypeScript) e quer aprender Rust, este guia é para você. Vamos traduzir os conceitos que você já domina do ecossistema JS para seus equivalentes em Rust, mostrando lado a lado como o código se compara. Rust pode parecer muito diferente no início, mas muitos conceitos fundamentais possuem paralelos diretos.

## Variáveis: `let`/`const` vs `let`/`let mut`

Ambas as linguagens usam `let`, mas com semânticas diferentes.

| Conceito | JavaScript | Rust |
|---|---|---|
| Imutável | `const x = 10;` | `let x = 10;` |
| Mutável | `let x = 10;` | `let mut x = 10;` |
| Constante global | `const PI = 3.14;` | `const PI: f64 = 3.14;` |
| Sem tipo declarado | `let x = 10;` | `let x = 10;` (tipo inferido) |
| Com tipo declarado | `let x: number = 10;` (TS) | `let x: i32 = 10;` |

**JavaScript:**
```javascript
let nome = "Maria";
nome = "Ana";           // ok, let permite reatribuição
const idade = 30;
// idade = 31;          // erro: const não permite reatribuição

let valor = 42;
valor = "texto";         // JS permite mudar o tipo
```

**Rust:**
```rust
let mut nome = String::from("Maria");
nome = String::from("Ana");  // ok, mut permite reatribuição
let idade = 30;
// idade = 31;                // erro: sem mut, não permite reatribuição

let valor = 42;
// valor = "texto";           // ERRO: tipos incompatíveis

// Shadowing permite "redeclarar"
let valor = "texto";          // ok, novo binding com shadowing
```

Em Rust, `let` (sem `mut`) se comporta como `const` do JavaScript. E `let mut` se comporta como `let` do JavaScript. Porém, diferente do JS, Rust nunca permite mudar o tipo de uma variável mutável -- apenas shadowing permite isso.

## `undefined`/`null` vs `Option<T>`

JavaScript tem dois tipos para "ausência de valor": `undefined` e `null`. Rust unifica isso em `Option<T>`, verificado em tempo de compilação.

| Conceito | JavaScript | Rust |
|---|---|---|
| Sem valor | `undefined` / `null` | `None` |
| Com valor | valor direto | `Some(valor)` |
| Checagem | `if (x != null)` | `if let Some(v) = x` |
| Acesso seguro | `x?.prop` (optional chaining) | `x.map(\|v\| v.prop)` |
| Valor padrão | `x ?? "default"` | `x.unwrap_or("default")` |

**JavaScript:**
```javascript
function buscarUsuario(id) {
    const usuarios = { 1: "Ana", 2: "Bruno" };
    return usuarios[id] ?? null;
}

const nome = buscarUsuario(3);
if (nome !== null) {
    console.log(`Encontrado: ${nome}`);
} else {
    console.log("Não encontrado");
}

// Optional chaining
const tamanho = nome?.length ?? 0;
```

**Rust:**
```rust
use std::collections::HashMap;

fn buscar_usuario(id: u32) -> Option<String> {
    let mut usuarios = HashMap::new();
    usuarios.insert(1, String::from("Ana"));
    usuarios.insert(2, String::from("Bruno"));
    usuarios.get(&id).cloned()
}

// Pattern matching
match buscar_usuario(3) {
    Some(nome) => println!("Encontrado: {}", nome),
    None => println!("Não encontrado"),
}

// Equivalente ao ?? (nullish coalescing)
let tamanho = buscar_usuario(3)
    .map(|n| n.len())
    .unwrap_or(0);
```

A grande vantagem do `Option<T>`: o compilador Rust garante que você trate o caso `None`. Em JavaScript, esquecer de checar `null` ou `undefined` causa erros em runtime como `TypeError: Cannot read property of undefined`.

## `try`/`catch` vs `Result<T, E>`

JavaScript usa exceções com `try/catch`. Rust usa o tipo `Result<T, E>` com o operador `?`.

**JavaScript:**
```javascript
async function carregarDados(url) {
    try {
        const resposta = await fetch(url);
        if (!resposta.ok) {
            throw new Error(`HTTP ${resposta.status}`);
        }
        const dados = await resposta.json();
        return dados;
    } catch (erro) {
        console.error("Falha ao carregar:", erro.message);
        return null;
    }
}
```

**Rust:**
```rust
use reqwest;
use serde::Deserialize;

#[derive(Deserialize)]
struct Dados {
    titulo: String,
}

async fn carregar_dados(url: &str) -> Result<Dados, reqwest::Error> {
    let dados = reqwest::get(url)
        .await?           // propaga erro de conexão
        .json::<Dados>()
        .await?;          // propaga erro de parse
    Ok(dados)
}

// Uso
#[tokio::main]
async fn main() {
    match carregar_dados("https://api.exemplo.com/dados").await {
        Ok(dados) => println!("Título: {}", dados.titulo),
        Err(e) => eprintln!("Falha ao carregar: {}", e),
    }
}
```

O operador `?` em Rust funciona como um `throw` automático que propaga o erro para o chamador. A diferença crucial: em JavaScript, qualquer função pode lançar uma exceção silenciosamente. Em Rust, `Result` no tipo de retorno torna o erro visível e obrigatório de tratar.

## Promises vs Futures

Promises do JavaScript e Futures do Rust são conceitualmente similares, mas com diferenças importantes.

| Conceito | JavaScript | Rust (Tokio) |
|---|---|---|
| Criar async | `async function f()` | `async fn f()` |
| Aguardar | `await promise` | `future.await` |
| Executar em paralelo | `Promise.all([...])` | `tokio::join!(...)` |
| Primeira a resolver | `Promise.race([...])` | `tokio::select!(...)` |
| Timeout | `Promise.race([p, timeout])` | `tokio::time::timeout()` |

**JavaScript:**
```javascript
async function buscarDados() {
    const [usuarios, posts] = await Promise.all([
        fetch("/api/usuarios").then(r => r.json()),
        fetch("/api/posts").then(r => r.json()),
    ]);
    return { usuarios, posts };
}
```

**Rust:**
```rust
async fn buscar_dados() -> Result<(Vec<Usuario>, Vec<Post>), reqwest::Error> {
    let (usuarios, posts) = tokio::join!(
        async { reqwest::get("/api/usuarios").await?.json().await },
        async { reqwest::get("/api/posts").await?.json().await },
    );
    Ok((usuarios?, posts?))
}
```

Uma diferença fundamental: em JavaScript, uma Promise começa a executar imediatamente ao ser criada. Em Rust, um Future é *lazy* -- só executa quando alguém faz `.await` nele ou o submete a um runtime com `tokio::spawn`.

## `npm` vs Cargo

| Tarefa | JavaScript (npm) | Rust (Cargo) |
|---|---|---|
| Iniciar projeto | `npm init` | `cargo new projeto` |
| Arquivo de config | `package.json` | `Cargo.toml` |
| Lock file | `package-lock.json` | `Cargo.lock` |
| Instalar dependências | `npm install` | `cargo build` |
| Adicionar pacote | `npm install express` | `cargo add actix-web` |
| Executar | `node index.js` / `npm start` | `cargo run` |
| Testar | `npm test` (jest/vitest) | `cargo test` |
| Formatar | `npx prettier --write .` | `cargo fmt` |
| Lint | `npx eslint .` | `cargo clippy` |
| Build produção | `npm run build` (webpack, etc.) | `cargo build --release` |
| Repositório | npmjs.com | crates.io |

Cargo combina as funcionalidades de npm, webpack, jest, prettier e eslint em uma única ferramenta integrada. Não há necessidade de configurar bundlers ou test runners separados.

## Objetos vs Structs

Objetos JavaScript são dinâmicos e flexíveis. Structs em Rust são tipados e fixos.

**JavaScript:**
```javascript
const usuario = {
    nome: "Carlos",
    email: "carlos@email.com",
    idade: 28,
};

// Adicionar propriedade dinâmica
usuario.ativo = true;

// Destructuring
const { nome, email } = usuario;

// Spread
const atualizado = { ...usuario, idade: 29 };
```

**Rust:**
```rust
#[derive(Debug, Clone)]
struct Usuario {
    nome: String,
    email: String,
    idade: u32,
}

let usuario = Usuario {
    nome: String::from("Carlos"),
    email: String::from("carlos@email.com"),
    idade: 28,
};

// Não é possível adicionar campos dinamicamente

// Destructuring
let Usuario { nome, email, .. } = &usuario;

// Struct update syntax (similar ao spread)
let atualizado = Usuario {
    idade: 29,
    ..usuario.clone()
};
```

## Prototypes vs Traits

A cadeia de protótipos do JavaScript e as traits de Rust resolvem o mesmo problema: compartilhar comportamento entre tipos diferentes.

**JavaScript:**
```javascript
class Animal {
    constructor(nome) {
        this.nome = nome;
    }
}

class Cachorro extends Animal {
    falar() {
        return `${this.nome} diz: Au au!`;
    }
}

class Gato extends Animal {
    falar() {
        return `${this.nome} diz: Miau!`;
    }
}

// Duck typing
function fazerFalar(animal) {
    console.log(animal.falar());
}
```

**Rust:**
```rust
trait Falante {
    fn falar(&self) -> String;
}

struct Cachorro {
    nome: String,
}

struct Gato {
    nome: String,
}

impl Falante for Cachorro {
    fn falar(&self) -> String {
        format!("{} diz: Au au!", self.nome)
    }
}

impl Falante for Gato {
    fn falar(&self) -> String {
        format!("{} diz: Miau!", self.nome)
    }
}

// Polimorfismo via trait
fn fazer_falar(animal: &dyn Falante) {
    println!("{}", animal.falar());
}
```

Rust usa composição em vez de herança. Traits definem contratos explícitos, semelhante a interfaces do TypeScript.

## Callbacks vs Closures

Ambas as linguagens suportam closures, mas Rust exige anotar como a closure captura variáveis do ambiente.

**JavaScript:**
```javascript
const numeros = [1, 2, 3, 4, 5];

// Map/Filter/Reduce
const resultado = numeros
    .filter(n => n % 2 === 0)
    .map(n => n * 2)
    .reduce((acc, n) => acc + n, 0);

console.log(resultado); // 12

// Callback
function executarComAtraso(callback, ms) {
    setTimeout(callback, ms);
}

executarComAtraso(() => console.log("Executado!"), 1000);
```

**Rust:**
```rust
let numeros = vec![1, 2, 3, 4, 5];

// Map/Filter/Fold (equivalente ao reduce)
let resultado: i32 = numeros.iter()
    .filter(|&&n| n % 2 == 0)
    .map(|&n| n * 2)
    .sum();

println!("{}", resultado); // 12

// Closures como parâmetros
fn executar_com_atraso<F: FnOnce()>(callback: F, ms: u64) {
    std::thread::sleep(std::time::Duration::from_millis(ms));
    callback();
}

executar_com_atraso(|| println!("Executado!"), 1000);
```

Rust tem tres tipos de closures: `Fn` (emprestam dados imutavelmente), `FnMut` (emprestam dados mutavelmente) e `FnOnce` (consomem os dados capturados). Em JavaScript, closures sempre capturam por referência, sem distinção.

## Tipos TypeScript vs Tipos Rust

Se você usa TypeScript, muitos conceitos de tipos traduzem diretamente para Rust.

| TypeScript | Rust |
|---|---|
| `number` | `i32`, `f64`, `u32`, etc. |
| `string` | `String` / `&str` |
| `boolean` | `bool` |
| `T[]` / `Array<T>` | `Vec<T>` |
| `Record<K, V>` | `HashMap<K, V>` |
| `T \| null` | `Option<T>` |
| `interface` | `trait` |
| `type` (union) | `enum` |
| `generic<T>` | `<T>` |
| `as` (type assertion) | Pattern matching / `as` (conversão numérica) |

**TypeScript:**
```typescript
interface Serializavel {
    toJSON(): string;
}

type Resultado<T> =
    | { sucesso: true; dados: T }
    | { sucesso: false; erro: string };

function processar<T extends Serializavel>(
    item: T
): Resultado<string> {
    try {
        return { sucesso: true, dados: item.toJSON() };
    } catch (e) {
        return { sucesso: false, erro: String(e) };
    }
}
```

**Rust:**
```rust
use serde::Serialize;

trait Serializavel {
    fn to_json(&self) -> Result<String, serde_json::Error>;
}

enum Resultado<T> {
    Sucesso(T),
    Erro(String),
}

fn processar<T: Serializavel>(item: &T) -> Resultado<String> {
    match item.to_json() {
        Ok(json) => Resultado::Sucesso(json),
        Err(e) => Resultado::Erro(e.to_string()),
    }
}
```

Os enums de Rust substituem as union types do TypeScript de forma mais poderosa, pois cada variante pode carregar dados diferentes e o compilador garante que todos os casos sejam tratados no `match`.

## JSON com `serde_json`

Trabalhar com JSON é parte do dia a dia em JavaScript. Em Rust, a crate `serde` com `serde_json` fornece serialização/desserialização poderosa.

**JavaScript:**
```javascript
const json = '{"nome": "Ana", "idade": 25}';
const obj = JSON.parse(json);
console.log(obj.nome);

const novoJson = JSON.stringify({ nome: "Bruno", idade: 30 });
```

**Rust:**
```rust
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Serialize, Deserialize, Debug)]
struct Pessoa {
    nome: String,
    idade: u32,
}

fn main() -> Result<(), serde_json::Error> {
    // Desserializar (parse)
    let json = r#"{"nome": "Ana", "idade": 25}"#;
    let pessoa: Pessoa = serde_json::from_str(json)?;
    println!("{}", pessoa.nome);

    // Serializar (stringify)
    let nova_pessoa = Pessoa {
        nome: String::from("Bruno"),
        idade: 30,
    };
    let novo_json = serde_json::to_string(&nova_pessoa)?;
    println!("{}", novo_json);

    // Também é possível trabalhar com JSON dinâmico
    let valor: serde_json::Value = serde_json::from_str(json)?;
    println!("{}", valor["nome"]);

    Ok(())
}
```

A abordagem do Rust com `serde` oferece desserialização tipada -- erros de formato são capturados em tempo de parse, não quando você acessa um campo que não existe.

## Node.js vs Rust para Backends

Ambas as linguagens são populares para backends. Vamos comparar um servidor HTTP simples.

**Node.js (Express):**
```javascript
const express = require("express");
const app = express();
app.use(express.json());

let tarefas = [];

app.get("/tarefas", (req, res) => {
    res.json(tarefas);
});

app.post("/tarefas", (req, res) => {
    const tarefa = req.body;
    tarefas.push(tarefa);
    res.status(201).json(tarefa);
});

app.listen(8080, () => {
    console.log("Servidor rodando na porta 8080");
});
```

**Rust (Actix-web):**
```rust
use actix_web::{web, App, HttpServer, HttpResponse};
use serde::{Deserialize, Serialize};
use std::sync::Mutex;

#[derive(Serialize, Deserialize, Clone)]
struct Tarefa {
    titulo: String,
    concluida: bool,
}

struct AppState {
    tarefas: Mutex<Vec<Tarefa>>,
}

async fn listar(data: web::Data<AppState>) -> HttpResponse {
    let tarefas = data.tarefas.lock().unwrap();
    HttpResponse::Ok().json(tarefas.clone())
}

async fn criar(
    data: web::Data<AppState>,
    tarefa: web::Json<Tarefa>,
) -> HttpResponse {
    let mut tarefas = data.tarefas.lock().unwrap();
    tarefas.push(tarefa.into_inner());
    HttpResponse::Created().finish()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let state = web::Data::new(AppState {
        tarefas: Mutex::new(Vec::new()),
    });

    println!("Servidor rodando na porta 8080");
    HttpServer::new(move || {
        App::new()
            .app_data(state.clone())
            .route("/tarefas", web::get().to(listar))
            .route("/tarefas", web::post().to(criar))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}
```

O código Rust é mais verboso, mas oferece vantagens significativas: sem garbage collector (latência previsível), segurança de memória garantida em compilação e desempenho consideravelmente maior. Benchmarks consistentemente mostram que frameworks Rust como Actix-web superam Express/Fastify por uma margem expressiva.

## Conclusão

A transição de JavaScript para Rust envolve aprender conceitos novos como ownership, lifetimes e tipagem estática rigorosa. No entanto, muitos padrões são familiares: closures, iteradores, async/await e um gerenciador de pacotes moderno.

**Dicas para a transição:**

1. **Se você usa TypeScript, está em vantagem** -- o hábito de pensar em tipos traduz diretamente para Rust.
2. **Ownership e borrowing** -- esse é o conceito mais novo. Dedique tempo para entender o borrow checker; as mensagens de erro do compilador Rust são excepcionalmente claras e educativas.
3. **Comece com `cargo`** -- a experiência é superior ao ecossistema fragmentado do npm/webpack/jest. Tudo funciona de forma integrada.
4. **`Option` e `Result` são seus novos melhores amigos** -- substituem o caos de `null`/`undefined` e exceções silenciosas do JavaScript.
5. **Use `serde` para JSON** -- a experiência é tão boa quanto `JSON.parse`/`JSON.stringify`, mas com segurança de tipos.
6. **Explore WebAssembly** -- Rust compila para WASM, permitindo que você use Rust no frontend junto com JavaScript.

A curva de aprendizado compensa rapidamente: código mais seguro, mais rápido e com menos bugs em produção. Bem-vindo ao Rust!

---

Está migrando de outras linguagens para Rust? Confira também nossos portais irmãos:

- <a href="https://python.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Python Brasil Dev</a> — guias de transição e comparações entre Python e Rust
- <a href="https://kotlin.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'kotlin.dev.br' })">Kotlin Brasil Dev</a> — Kotlin como alternativa moderna para quem vem da JVM
