Rust vs TypeScript: Qual Melhor Backend 2026? | Rust Brasil

Rust vs TypeScript para backend: Node.js vs nativo, performance, tipos e quando migrar. Também compara Cargo vs TypeScript.

Introdução

TypeScript se tornou a linguagem dominante para desenvolvimento web full-stack, especialmente no backend com Node.js, Deno e Bun. TypeScript oferece tipagem estática sobre JavaScript, um ecossistema npm gigantesco e a capacidade de compartilhar código entre frontend e backend. Rust, por outro lado, compila para código nativo e oferece performance 10-50x superior, segurança de memória garantida e binários autossuficientes.

Este artigo é para desenvolvedores TypeScript/Node.js que estão considerando Rust para o backend, ou que querem entender quando faz sentido migrar componentes críticos. Se você já trabalha com Express, Fastify ou NestJS e quer saber o que Rust pode oferecer, continue lendo.

Tabela Comparativa

AspectoRustTypeScript (Node.js)
ExecuçãoCódigo nativo (LLVM)V8 JIT (JavaScript)
TipagemEstática, verificada em compilaçãoEstática (apenas em dev, apagada em runtime)
Null/undefinedOption<T> (sem null)strictNullChecks (opt-in)
Performance10-50x mais rápido (CPU-bound)Suficiente para I/O-bound
Asyncasync/await com Tokio (multi-thread)Event loop single-thread + worker threads
Gerenciamento de memóriaOwnership (determinístico)GC do V8
Ecossistemacrates.io (~150k)npm (~2.5M pacotes)
Startup< 1 ms~50-200 ms (Node.js)
Memória (servidor idle)~5 MB~40-80 MB
Frameworks webAxum, Actix WebExpress, Fastify, NestJS
Hot reloadNão nativo (cargo-watch)Nativo (nodemon, tsx)

Sistemas de Tipos: Compilação vs Apagamento

A diferença mais sutil entre Rust e TypeScript está nos sistemas de tipos.

TypeScript: Tipos que Desaparecem

interface Usuario {
  id: number;
  nome: string;
  email: string;
  ativo: boolean;
}

function processarUsuario(usuario: Usuario): string {
  return `${usuario.nome} <${usuario.email}>`;
}

// Em runtime, o tipo Usuario NÃO EXISTE
// Nada impede que dados inválidos cheguem via API
const dados = JSON.parse('{"id": "abc", "nome": 123}');
processarUsuario(dados); // Sem erro de tipo em runtime!

TypeScript apaga todos os tipos em tempo de compilação. Em runtime, o código é JavaScript puro — qualquer dado inválido que chegue via rede, banco de dados ou arquivo JSON passa direto pelos tipos.

Rust: Tipos que Garantem

use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct Usuario {
    id: u64,
    nome: String,
    email: String,
    ativo: bool,
}

fn processar_usuario(usuario: &Usuario) -> String {
    format!("{} <{}>", usuario.nome, usuario.email)
}

fn main() {
    // serde valida os tipos em RUNTIME durante a deserialização
    let json = r#"{"id": "abc", "nome": 123}"#;
    let resultado: Result<Usuario, _> = serde_json::from_str(json);

    match resultado {
        Ok(usuario) => println!("{}", processar_usuario(&usuario)),
        Err(e) => println!("Erro de parsing: {e}"),
        // "Erro de parsing: invalid type: string \"abc\", expected u64 at line 1 column 12"
    }
}

Em Rust, serde verifica os tipos durante a deserialização. Se os dados não correspondem ao tipo esperado, você recebe um erro explícito em vez de um bug silencioso.

Exemplo Prático: API REST com Validação

TypeScript com Fastify

import Fastify from "fastify";
import { z } from "zod";

const app = Fastify();

const TarefaSchema = z.object({
  titulo: z.string().min(1).max(200),
  descricao: z.string().optional(),
  prioridade: z.enum(["baixa", "media", "alta"]),
});

type Tarefa = z.infer<typeof TarefaSchema> & { id: number };

const tarefas: Tarefa[] = [];
let proximoId = 1;

app.get("/tarefas", async () => {
  return tarefas;
});

app.post("/tarefas", async (request, reply) => {
  const resultado = TarefaSchema.safeParse(request.body);
  if (!resultado.success) {
    return reply.status(400).send({ erros: resultado.error.issues });
  }

  const tarefa: Tarefa = { id: proximoId++, ...resultado.data };
  tarefas.push(tarefa);
  return reply.status(201).send(tarefa);
});

app.listen({ port: 3000 }, () => {
  console.log("Servidor rodando na porta 3000");
});

Rust com Axum

use axum::{
    extract::State,
    http::StatusCode,
    routing::{get, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};

#[derive(Clone, Serialize)]
struct Tarefa {
    id: u64,
    titulo: String,
    descricao: Option<String>,
    prioridade: Prioridade,
}

#[derive(Clone, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
enum Prioridade {
    Baixa,
    Media,
    Alta,
}

#[derive(Deserialize)]
struct CriarTarefa {
    titulo: String,
    descricao: Option<String>,
    prioridade: Prioridade,
}

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

async fn listar(State(state): State<Arc<AppState>>) -> Json<Vec<Tarefa>> {
    let tarefas = state.tarefas.lock().unwrap();
    Json(tarefas.clone())
}

async fn criar(
    State(state): State<Arc<AppState>>,
    Json(dto): Json<CriarTarefa>,
) -> (StatusCode, Json<Tarefa>) {
    let mut id = state.proximo_id.lock().unwrap();
    *id += 1;
    let tarefa = Tarefa {
        id: *id,
        titulo: dto.titulo,
        descricao: dto.descricao,
        prioridade: dto.prioridade,
    };
    state.tarefas.lock().unwrap().push(tarefa.clone());
    (StatusCode::CREATED, Json(tarefa))
}

#[tokio::main]
async fn main() {
    let state = Arc::new(AppState {
        tarefas: Mutex::new(Vec::new()),
        proximo_id: Mutex::new(0),
    });

    let app = Router::new()
        .route("/tarefas", get(listar).post(criar))
        .with_state(state);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();
    axum::serve(listener, app).await.unwrap();
}

Note como Rust usa o enum Prioridade — se o JSON contiver um valor inválido como "urgente", a deserialização falha automaticamente com um erro claro. Em TypeScript, sem Zod (ou similar), esse valor passaria sem detecção.

Para mais exemplos de servidores HTTP em Rust, veja nossa receita de criar servidor HTTP.

Modelos Async: Event Loop vs Multi-Thread

Node.js: Single-Thread Event Loop

// Node.js usa um único thread para processar requisições
// Operações CPU-bound bloqueiam TODAS as requisições

import { createHash } from "crypto";

// Esta função bloqueia o event loop inteiro!
function hashPesado(dados: string): string {
  let resultado = dados;
  for (let i = 0; i < 100_000; i++) {
    resultado = createHash("sha256").update(resultado).digest("hex");
  }
  return resultado;
}

Rust com Tokio: Multi-Thread por Padrão

use sha2::{Sha256, Digest};
use tokio::task;

async fn hash_pesado(dados: String) -> String {
    // spawn_blocking move trabalho CPU-bound para thread pool separado
    task::spawn_blocking(move || {
        let mut resultado = dados;
        for _ in 0..100_000 {
            let mut hasher = Sha256::new();
            hasher.update(resultado.as_bytes());
            resultado = format!("{:x}", hasher.finalize());
        }
        resultado
    })
    .await
    .unwrap()
}

O Tokio usa múltiplos threads por padrão (um por core da CPU), e operações CPU-bound podem ser delegadas para um pool separado sem bloquear o processamento de I/O.

Comparação de Performance

Benchmarks de Servidor Web

MétricaRust (Axum)Node.js (Fastify)Bun
Requisições/s (JSON)~850.000~80.000~120.000
Latência p990,8 ms5 ms3 ms
Startup< 5 ms~150 ms~50 ms
Memória idle~5 MB~50 MB~30 MB
Memória sob carga~25 MB~200 MB~100 MB
Binário/Bundle~8 MB~200 MB (node_modules)~180 MB

Onde a Diferença Importa

Para APIs típicas (CRUD, JSON, banco de dados), a performance do Node.js/Bun é frequentemente suficiente. O gargalo real é o banco de dados ou serviços externos, não o framework.

A diferença se torna crítica em:

  • Processamento CPU-bound: parsing, compressão, criptografia, transformação de dados
  • Carga muito alta: >100k requisições/segundo
  • Latência ultra-baixa: sistemas financeiros, gaming, IoT em tempo real
  • Serverless: onde startup e memória são cobrados por milissegundo

Quando Devs TypeScript Devem Aprender Rust

Rust é especialmente valioso para desenvolvedores TypeScript que:

  1. Constroem ferramentas de build: o próprio ecossistema JS está migrando para Rust (SWC, Turbopack, Biome, oxlint)
  2. Precisam de WebAssembly: Rust é a linguagem mais popular para Wasm
  3. Atingem limites de performance: quando o V8 não é rápido o suficiente
  4. Querem entender sistemas: como memória, threads e networking funcionam de verdade
  5. Desenvolvem CLIs: ferramentas como ripgrep, fd, bat são escritas em Rust

A boa notícia: se você já usa TypeScript com strict: true, muitos conceitos de Rust vão parecer familiares — pattern matching, tipos algébricos (enums), Result/Option vs unions, e generics com constraints.

Quando Usar TypeScript

Escolha TypeScript quando:

  • Full-stack com compartilhamento de código: mesmo modelo no frontend e backend
  • Prototipagem rápida: npm tem pacote para tudo, hot reload é instantâneo
  • A equipe é web-first: a maioria dos devs web já conhece TypeScript
  • O projeto é I/O-bound: APIs que principalmente fazem queries no banco e chamam serviços externos
  • Ecossistema específico: frameworks como Next.js, Remix, Nuxt

Quando Usar Rust

Escolha Rust quando:

  • Performance é diferencial competitivo: sistemas que precisam ser rápidos de verdade
  • Confiabilidade é obrigatória: o compilador pega mais bugs que qualquer suite de testes
  • Recursos são limitados: serverless, edge computing, embarcados
  • WebAssembly: lógica compartilhada entre server e browser via Wasm
  • Ferramentas developer: CLIs, linters, bundlers, compiladores

Conclusão e Recomendação

Para a maioria das APIs web, TypeScript com Fastify ou Hono continua sendo a escolha mais produtiva em 2026. O ecossistema é imbatível, a velocidade de desenvolvimento é alta e a performance é suficiente para 90% dos casos de uso.

Aprenda Rust se você é desenvolvedor TypeScript — não necessariamente para substituir TS, mas para expandir sua capacidade. Comece com CLIs simples ou módulos WebAssembly. Gradualmente, você vai identificar cenários onde Rust é claramente superior.

Para novos projetos de alta performance, como plataformas de dados, processadores de streaming ou ferramentas de infraestrutura, comece em Rust. O investimento compensa com menor custo operacional e maior confiabilidade.


Veja Também