---
title: "Zig para Programadores JavaScript: Do V8 ao Metal"
url: "https://ziglang.com.br/tutoriais/zig-para-programadores-javascript/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-para-programadores-javascript.MD"
description: "Guia para devs JavaScript aprenderem Zig. Entenda como Bun usa Zig, compare conceitos JS com Zig e construa código nativo."
date: "2026-02-20"
author: "Zig Brasil"
---

# Zig para Programadores JavaScript: Do V8 ao Metal

Guia para devs JavaScript aprenderem Zig. Entenda como Bun usa Zig, compare conceitos JS com Zig e construa código nativo.


Se você trabalha com JavaScript e já ouviu falar do Bun, provavelmente se perguntou: o que é essa linguagem usada para construí-lo? A resposta é **zig lang**, uma linguagem de programação de sistemas que está ganhando tração no ecossistema JavaScript. A **linguagem Zig** combina a performance do C com uma experiência de desenvolvimento moderna, e neste guia vamos explorar como seus conhecimentos de JavaScript se traduzem para Zig.

## Por Que Desenvolvedores JS Deveriam Conhecer Zig

O ecossistema JavaScript está cada vez mais conectado ao Zig. Veja por quê:

- **Bun é escrito em Zig**: O runtime JavaScript mais rápido do mercado foi construído com Zig. Entender Zig é entender o motor por trás do Bun.
- **Performance nativa**: Quando seu servidor Node.js não aguenta a carga, reescrever partes críticas em Zig pode resolver o gargalo.
- **WebAssembly**: Zig compila diretamente para WASM, permitindo executar código de alta performance no navegador.
- **Node.js addons**: Crie extensões nativas para Node.js com Zig, substituindo C++ como linguagem padrão para addons.
- **Entendimento profundo**: Compreender como o V8 e o event loop funcionam por baixo fica muito mais fácil quando você conhece programação de sistemas.

## Diferenças Fundamentais: JavaScript vs Zig

| Aspecto | JavaScript | Zig |
|---|---|---|
| Execução | V8/SpiderMonkey (JIT) | Compilado (LLVM) |
| Tipagem | Dinâmica, fraca | Estática, forte |
| Memória | Garbage Collector | Gerenciamento manual |
| Concorrência | Event loop (single thread) | Threads reais |
| Pacotes | npm/yarn/pnpm | zig build + packages |
| Null | `null` e `undefined` | `null` (optional explícito) |
| Erros | `throw/try/catch` | Error unions |
| OOP | Prototypes/Classes | Structs |

## Variáveis: let/const vs var/const

**JavaScript:**
```javascript
let nome = "Maria";
const idade = 28;
let contador = 0;
contador += 1;

// Tipo é inferido e pode mudar
let valor = 42;
valor = "quarenta e dois"; // OK em JS
```

**Zig:**
```zig
var nome: []const u8 = "Maria";
const idade: u32 = 28;
var contador: u32 = 0;
contador += 1;

// Tipo é fixo após declaração
var valor: i32 = 42;
// valor = "quarenta e dois"; // ERRO de compilação!
```

Em Zig, `const` é verdadeiramente imutável (diferente do `const` de JS que permite mutação de objetos). A variável `var` permite reatribuição, mas o tipo nunca muda. Essa distinção é especialmente importante quando se trabalha com [gerenciamento de memória em Zig](/tutoriais/gerenciamento-de-memoria-zig/), onde controlar mutabilidade é essencial.

Zig também tem inferência de tipos quando o valor inicial é claro:

```zig
const nome = "Maria";       // tipo inferido: *const [5:0]u8
const idade = @as(u32, 28); // tipo explicitado via @as
```

## Funções

**JavaScript:**
```javascript
function somar(a, b) {
    return a + b;
}

const multiplicar = (a, b) => a * b;

// Função com valor padrão
function cumprimentar(nome = "mundo") {
    return `Olá, ${nome}!`;
}
```

**Zig:**
```zig
const std = @import("std");

fn somar(a: i32, b: i32) i32 {
    return a + b;
}

// Não existe arrow function, mas funções podem ser constantes
const multiplicar = struct {
    fn call(a: i32, b: i32) i32 {
        return a * b;
    }
}.call;

pub fn main() void {
    const resultado = somar(3, 5);
    std.debug.print("Soma: {}\n", .{resultado});

    const produto = multiplicar(4, 6);
    std.debug.print("Produto: {}\n", .{produto});
}
```

Diferenças importantes:
- Parâmetros e retorno sempre têm tipos explícitos.
- Não existem arrow functions, mas Zig tem formas de passar funções como valores.
- Não existem valores padrão para parâmetros (mas há técnicas com structs de opções).

## Closures e Callbacks

JavaScript depende muito de closures e callbacks. Zig tem uma abordagem diferente.

**JavaScript:**
```javascript
function criarContador() {
    let count = 0;
    return {
        incrementar: () => ++count,
        valor: () => count
    };
}

const contador = criarContador();
contador.incrementar();
console.log(contador.valor()); // 1

// Callback
[1, 2, 3].map(x => x * 2); // [2, 4, 6]
```

**Zig:**
```zig
const std = @import("std");

const Contador = struct {
    count: i32 = 0,

    pub fn incrementar(self: *Contador) void {
        self.count += 1;
    }

    pub fn valor(self: Contador) i32 {
        return self.count;
    }
};

pub fn main() void {
    var contador = Contador{};
    contador.incrementar();
    std.debug.print("Valor: {}\n", .{contador.valor()});
}
```

Zig não tem closures como JavaScript. Em vez disso, usamos structs com estado. Para operações como `map`, Zig usa loops explícitos:

```zig
const std = @import("std");

pub fn main() void {
    const entrada = [_]i32{ 1, 2, 3 };
    var saida: [3]i32 = undefined;

    for (entrada, 0..) |valor, i| {
        saida[i] = valor * 2;
    }

    // saida agora é { 2, 4, 6 }
    for (saida) |v| {
        std.debug.print("{} ", .{v});
    }
}
```

## Async/Await: Event Loop vs Threads

Uma das maiores diferenças conceituais está na concorrência.

**JavaScript:**
```javascript
async function buscarDados(url) {
    try {
        const response = await fetch(url);
        const dados = await response.json();
        return dados;
    } catch (erro) {
        console.error("Falha:", erro);
    }
}

// Promise.all para paralelismo
const [usuarios, posts] = await Promise.all([
    buscarDados("/api/usuarios"),
    buscarDados("/api/posts")
]);
```

Em Zig, a concorrência é baseada em threads reais do sistema operacional:

```zig
const std = @import("std");

fn tarefaPesada(id: u32) void {
    std.debug.print("Thread {} iniciada\n", .{id});
    // Simular trabalho pesado
    std.time.sleep(1_000_000_000); // 1 segundo
    std.debug.print("Thread {} finalizada\n", .{id});
}

pub fn main() !void {
    var threads: [4]std.Thread = undefined;

    // Criar 4 threads
    for (0..4) |i| {
        threads[i] = try std.Thread.spawn(.{}, tarefaPesada, .{@as(u32, @intCast(i))});
    }

    // Aguardar todas finalizarem (equivalente conceitual ao Promise.all)
    for (&threads) |*t| {
        t.join();
    }

    std.debug.print("Todas as threads finalizaram\n", .{});
}
```

Em JavaScript, o event loop gerencia I/O assíncrono em uma única thread. Em Zig, você tem controle direto sobre threads do sistema operacional, podendo executar código verdadeiramente paralelo em múltiplos núcleos.

## Tratamento de Erros

**JavaScript:**
```javascript
function dividir(a, b) {
    if (b === 0) throw new Error("Divisão por zero");
    return a / b;
}

try {
    const resultado = dividir(10, 0);
    console.log(resultado);
} catch (e) {
    console.error(e.message);
}
```

**Zig:**
```zig
const std = @import("std");

const MathError = error{
    DivisaoPorZero,
};

fn dividir(a: f64, b: f64) MathError!f64 {
    if (b == 0.0) return MathError.DivisaoPorZero;
    return a / b;
}

pub fn main() void {
    const resultado = dividir(10.0, 0.0) catch |err| {
        std.debug.print("Erro: {}\n", .{err});
        return;
    };
    std.debug.print("Resultado: {d}\n", .{resultado});
}
```

A diferença fundamental: em JavaScript, qualquer função pode lançar uma exceção a qualquer momento, e não há como saber pelo tipo. Em Zig, se uma função pode falhar, o tipo de retorno deixa isso explícito (`MathError!f64`). O compilador garante que todo erro seja tratado. Para se aprofundar nesse tema, veja o guia completo sobre [tratamento de erros em Zig](/tutoriais/tratamento-de-erros-em-zig/).

O operador `try` em Zig propaga erros automaticamente, similar a não usar try/catch em JavaScript e deixar a exceção subir:

```zig
fn processarDados() !void {
    const valor = try dividir(10.0, 2.0); // Se falhar, propaga o erro
    std.debug.print("Valor: {d}\n", .{valor});
}
```

## Objetos e JSON: Prototypes vs Structs

**JavaScript:**
```javascript
const usuario = {
    nome: "Carlos",
    idade: 30,
    ativo: true
};

const json = JSON.stringify(usuario);
const parsed = JSON.parse(json);
```

**Zig:**
```zig
const std = @import("std");

const Usuario = struct {
    nome: []const u8,
    idade: u32,
    ativo: bool,
};

pub fn main() !void {
    const usuario = Usuario{
        .nome = "Carlos",
        .idade = 30,
        .ativo = true,
    };

    // Serializar para JSON
    var buf: [256]u8 = undefined;
    var stream = std.io.fixedBufferStream(&buf);
    try std.json.stringify(usuario, .{}, stream.writer());

    const json_str = stream.getWritten();
    std.debug.print("JSON: {s}\n", .{json_str});
}
```

Em JavaScript, objetos são dinâmicos e podem ter qualquer propriedade adicionada em tempo de execução. Em Zig, structs têm campos fixos definidos em tempo de compilação.

## Como o Bun Usa Zig

O Bun, criado por Jarred Sumner, é um runtime JavaScript que compete com Node.js e Deno. Ele escolheu Zig em vez de C++ ou Rust pelos seguintes motivos:

- **Interoperabilidade com C**: Zig pode chamar código C diretamente, sem FFI overhead. Isso foi essencial para integrar com o JavaScriptCore (motor JS do Safari/WebKit).
- **Performance previsível**: Sem garbage collector, o Bun pode garantir latências baixas e consistentes.
- **Alocadores customizados**: Zig permite usar diferentes estratégias de alocação de memória, o que o Bun explora para otimizar diferentes tipos de operação.
- **Compilação rápida**: O tempo de compilação de Zig é significativamente menor que C++, acelerando o desenvolvimento.

Exemplo conceitual de como o Bun usa Zig para processar requisições HTTP:

```zig
const std = @import("std");
const net = std.net;

pub fn main() !void {
    // Servidor TCP básico (conceito simplificado do que o Bun faz)
    var server = try net.StreamServer.init(.{});
    defer server.deinit();

    try server.listen(net.Address.parseIp("0.0.0.0", 3000) catch unreachable);
    std.debug.print("Servidor rodando na porta 3000\n", .{});

    while (true) {
        if (server.accept()) |conn| {
            defer conn.stream.close();
            // Processar requisição...
            _ = try conn.stream.write("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK");
        } else |_| {
            continue;
        }
    }
}
```

## WebAssembly: Usando Zig no Navegador

Uma das formas mais naturais de usar Zig como desenvolvedor JavaScript é compilando para WebAssembly.

```zig
// math.zig - será compilado para WASM
export fn fibonacci(n: u32) u32 {
    if (n <= 1) return n;
    var a: u32 = 0;
    var b: u32 = 1;
    var i: u32 = 2;
    while (i <= n) : (i += 1) {
        const temp = a + b;
        a = b;
        b = temp;
    }
    return b;
}

export fn fatorial(n: u32) u64 {
    var resultado: u64 = 1;
    var i: u32 = 2;
    while (i <= n) : (i += 1) {
        resultado *= @as(u64, i);
    }
    return resultado;
}
```

Compile para WASM:
```bash
zig build-lib math.zig -target wasm32-freestanding -dynamic -O ReleaseSmall
```

Use no JavaScript:
```javascript
async function carregarWasm() {
    const response = await fetch('math.wasm');
    const bytes = await response.arrayBuffer();
    const { instance } = await WebAssembly.instantiate(bytes);

    console.log(instance.exports.fibonacci(40));  // Resultado instantâneo
    console.log(instance.exports.fatorial(20));
}

carregarWasm();
```

## Construindo Addons Nativos para Node.js

Tradicionalmente, addons nativos para Node.js são escritos em C++. Com Zig, o processo fica mais simples.

```zig
// addon.zig
const c = @cImport({
    @cInclude("node_api.h");
});

export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) c.napi_value {
    // Registrar funções nativas aqui
    var fn_value: c.napi_value = undefined;
    _ = c.napi_create_function(env, "hello", 5, &helloNative, null, &fn_value);
    _ = c.napi_set_named_property(env, exports, "hello", fn_value);
    return exports;
}

fn helloNative(env: c.napi_env, info: c.napi_callback_info) c.napi_value {
    _ = info;
    var result: c.napi_value = undefined;
    _ = c.napi_create_string_utf8(env, "Olá do Zig!", 13, &result);
    return result;
}
```

No lado JavaScript:
```javascript
const addon = require('./zig-addon.node');
console.log(addon.hello()); // "Olá do Zig!"
```

## npm vs Zig Build System

| npm/package.json | Zig Build System |
|---|---|
| `npm init` | `zig init` |
| `package.json` | `build.zig` |
| `npm install pacote` | Dependências via `build.zig.zon` |
| `npm run build` | `zig build` |
| `npm test` | `zig build test` |
| `node_modules/` | Cache global de pacotes |

Um `build.zig.zon` típico (equivalente ao `package.json`):

```zig
.{
    .name = "meu-projeto",
    .version = "0.1.0",
    .dependencies = .{
        .zap = .{
            .url = "https://github.com/zigzap/zap/archive/v0.1.0.tar.gz",
            .hash = "...",
        },
    },
}
```

## Tabela de Referência Rápida

| JavaScript | Zig | Notas |
|---|---|---|
| `let x = 5` | `var x: i32 = 5` | Zig exige tipo ou inferência |
| `const x = 5` | `const x: i32 = 5` | `const` é realmente imutável |
| `console.log()` | `std.debug.print()` | Sintaxe de formatação diferente |
| `null` / `undefined` | `null` | Tipo optional `?T` |
| `Array` | `[N]T` ou `std.ArrayList` | Arrays fixos ou dinâmicos |
| `Object` | `struct` | Campos definidos em compilação |
| `Promise` | `std.Thread` | Paralelismo real |
| `JSON.parse` | `std.json.parseFromSlice` | Parsing tipado |
| `require/import` | `@import` | Sistema de módulos |
| `===` | `==` | Zig não tem coerção de tipos |

## Conclusão

Aprender Zig como desenvolvedor JavaScript abre uma dimensão completamente nova da programação. Você vai entender o que acontece por baixo do V8, como o Bun consegue ser tão rápido e como escrever código que roda sem runtime. A curva de aprendizado é real, mas cada conceito novo em Zig vai tornar você um desenvolvedor JavaScript mais consciente e eficiente.

## Leia Também

- [Como o Bun Foi Construído com Zig](/tutoriais/como-bun-foi-construido-zig)
- [Zig e WebAssembly: Guia Completo](/tutoriais/zig-webassembly-wasm)
- [Construindo um Servidor HTTP em Zig](/tutoriais/zig-http-server)
