---
title: "Zig para Desenvolvedores Rust: Guia de Transição Completo"
url: "https://ziglang.com.br/artigos/zig-para-desenvolvedores-rust/"
markdown_url: "https://ziglang.com.br/artigos/zig-para-desenvolvedores-rust.MD"
description: "Guia prático para desenvolvedores Rust migrarem para Zig. Comparação de conceitos: ownership, error handling, generics, async e mais. Exemplos lado a lado para facilitar a transição."
date: "2026-02-10"
author: ""
---

# Zig para Desenvolvedores Rust: Guia de Transição Completo

Guia prático para desenvolvedores Rust migrarem para Zig. Comparação de conceitos: ownership, error handling, generics, async e mais. Exemplos lado a lado para facilitar a transição.


Se você é um desenvolvedor Rust procurando aprender Zig, este guia é para você. Como Rustacean, você já tem uma base sólida em programação de sistemas — segurança de memória, zero-cost abstractions, e controle de baixo nível. Para revisar conceitos, erros comuns e projetos no ecossistema Rust em português, use também o <a href="https://rustlang.com.br/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust Lang Brasil</a>. A boa notícia é que muitos desses conceitos se aplicam a Zig, embora de formas diferentes.

Este guia foca nas **diferenças práticas** entre as linguagens, mostrando como traduzir seus padrões mentais de Rust para código Zig funcional.

> **Nota:** Se você procura uma comparação geral entre as linguagens, leia primeiro [Zig vs Rust: Qual Linguagem Escolher em 2026?](/artigos/zig-vs-rust/)

## Visão Geral: Do que Você Precisa Desaprender

### Mentalidade Rust → Mentalidade Zig

| Aspecto | Mentalidade Rust | Mentalidade Zig |
|---------|-----------------|-----------------|
| **Segurança** | "O compilador impede bugs" | "O programador é responsável, com ferramentas de debug" |
| **Abstração** | Abstrações ricas (traits, generics) | Simplicidade explícita (comptime quando necessário) |
| **Ownership** | Gerenciado pelo compilador | Gerenciado pelo programador via allocators |
| **Erros** | `Result<T, E>` e `Option<T>` | Error unions `!T` e tipos opcionais `?T` |
| **Metaprogramação** | Macros e derive | `comptime` — mesma linguagem |

## 1. Gerenciamento de Memória

### 1.1 A Grande Diferença: Ownership vs Allocators

Em Rust, o borrow checker gerencia memória automaticamente. Em Zig, você gerencia explicitamente via allocators.

**Rust — Ownership automático:**

```rust
fn process_data() {
    let data = vec![1, 2, 3, 4, 5]; // Vec aloca no heap
    // Memória liberada automaticamente quando 'data' sai do escopo
    println!("{:?}", data);
} // Drop automático
```

**Zig — Allocator explícito:**

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

fn processData(allocator: std.mem.Allocator) !void {
    // Alocação explícita
    const data = try allocator.alloc(i32, 5);
    defer allocator.free(data); // Liberação explícita
    
    // Preenche dados
    for (data, 0..) |*item, i| {
        item.* = @intCast(i + 1);
    }
    
    std.debug.print("{any}\n", .{data});
} // free chamado automaticamente pelo defer
```

**Padrão Zig para Rustaceans:**

1. **Sempre passe `allocator`** para funções que precisam alocar
2. **Use `defer`** para liberação (similar ao Drop implícito do Rust)
3. **Use `errdefer`** para liberação em caso de erro (similar a guard clauses)

### 1.2 GeneralPurposeAllocator — O "GC" de Debug do Zig

Para começar, use `GeneralPurposeAllocator`. Ele detecta vazamentos e double-free em debug builds:

```zig
pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer {
        const status = gpa.deinit();
        if (status == .leak) @panic("Memory leak detected!");
    }
    const allocator = gpa.allocator();
    
    // Use 'allocator' para todas as alocações
    const string = try allocator.dupe(u8, "Hello, Zig!");
    defer allocator.free(string);
    
    std.debug.print("{s}\n", .{string});
}
```

### 1.3 Arena Allocator — O "Owned" do Rust

Quando você quer agrupar alocações e liberar tudo de uma vez (similar a um `Vec<Box<T>>` que dropa tudo):

```zig
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); // Libera TUDO de uma vez
const allocator = arena.allocator();

// Faça várias alocações
const a = try allocator.alloc(u8, 100);
const b = try allocator.alloc(u8, 200);
const c = try allocator.dupe(u8, "texto");

// Não precisa de defer para cada um!
// Tudo é liberado quando arena.deinit() é chamado
```

### 1.4 Tabela de Conversão: Alocações

| Operação Rust | Equivalente Zig |
|--------------|-----------------|
| `Box::new(x)` | `try allocator.create(T)` + `defer allocator.destroy(ptr)` |
| `vec![1,2,3]` | `try allocator.alloc(T, n)` + preencher |
| `String::new()` | `std.ArrayList(u8)` ou `allocator.alloc(u8, n)` |
| `Vec::with_capacity(n)` | `try allocator.alloc(T, n)` |
| `vec.push(x)` | `array_list.append(x)` (use ArrayList para grow) |
| `drop(x)` | Implícito via `defer`/`errdefer` |
| `Box::leak(x)` | Use arena allocator ou `std.heap.page_allocator` |

## 2. Error Handling

### 2.1 Result<T, E> → Error Unions

**Rust:**

```rust
fn read_file(path: &str) -> Result<String, std::io::Error> {
    std::fs::read_to_string(path)
}

fn main() {
    match read_file("data.txt") {
        Ok(contents) => println!("{}", contents),
        Err(e) => eprintln!("Error: {}", e),
    }
}
```

**Zig:**

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

fn readFile(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {
    return std.fs.cwd().readFileAlloc(allocator, path, 1024 * 1024);
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    const contents = readFile(allocator, "data.txt") catch |err| {
        std.debug.print("Error: {}\n", .{err});
        return;
    };
    defer allocator.free(contents);
    
    std.debug.print("{s}\n", .{contents});
}
```

### 2.2 O Operador `try` — Similar ao `?` do Rust

Ambos usam `try`/`?` para propagar erros:

```rust
// Rust
fn process() -> Result<i32, Error> {
    let data = read_file("input.txt")?; // Propaga erro com '?'
    let parsed = parse_i32(&data)?;      // Propaga erro com '?'
    Ok(parsed * 2)
}
```

```zig
// Zig
fn process(allocator: std.mem.Allocator) !i32 {
    const data = try readFile(allocator, "input.txt"); // Propaga erro com 'try'
    defer allocator.free(data);
    
    const parsed = try std.fmt.parseInt(i32, data, 10); // Propaga erro com 'try'
    return parsed * 2;
}
```

### 2.3 Error Sets — Definindo Erros Específicos

**Rust:**

```rust
#[derive(Debug)]
enum MyError {
    InvalidInput,
    NotFound,
    PermissionDenied,
}

fn do_something() -> Result<i32, MyError> {
    Err(MyError::InvalidInput)
}
```

**Zig:**

```zig
const MyError = error{
    InvalidInput,
    NotFound,
    PermissionDenied,
};

fn doSomething() MyError!i32 {
    return error.InvalidInput;
}
```

### 2.4 errdefer — Limpeza em Caso de Erro

Equivalente a `scopeguard` ou `Drop` em código que pode falhar:

```zig
fn createResource(allocator: std.mem.Allocator) !Resource {
    const resource = try allocator.create(Resource);
    
    // Se a função retornar erro após este ponto, 'destroy' será chamado
    errdefer allocator.destroy(resource);
    
    resource.data = try allocator.alloc(u8, 100);
    
    // Se falhar aqui, errdefer acima executa
    resource.handle = try openHandle();
    
    // Sucesso — cancela o errdefer
    return resource;
}
```

## 3. Tipos e Generics

### 3.1 Structs Básicos

**Rust:**

```rust
struct Point {
    x: f64,
    y: f64,
}

impl Point {
    fn new(x: f64, y: f64) -> Self {
        Point { x, y }
    }
    
    fn distance_from_origin(&self) -> f64 {
        (self.x * self.x + self.y * self.y).sqrt()
    }
}
```

**Zig:**

```zig
const Point = struct {
    x: f64,
    y: f64,
    
    // "Método" — na verdade, função associada
    pub fn new(x: f64, y: f64) Point {
        return .{ .x = x, .y = y };
    }
    
    // Recebe self por valor (copia)
    pub fn distanceFromOrigin(self: Point) f64 {
        return @sqrt(self.x * self.x + self.y * self.y);
    }
    
    // Recebe self por ponteiro (modificável)
    pub fn translate(self: *Point, dx: f64, dy: f64) void {
        self.x += dx;
        self.y += dy;
    }
};
```

**Diferenças importantes:**

1. Não há `impl` blocks em Zig — métodos são declarados dentro da struct
2. `self` é explícito: `Point`, `*Point` (mutável), ou `*const Point` (imutável)
3. Construtores são convenção, não obrigatórios

### 3.2 Generics via comptime

**Rust:**

```rust
struct Container<T> {
    value: T,
}

impl<T> Container<T> {
    fn new(value: T) -> Self {
        Container { value }
    }
    
    fn get(&self) -> &T {
        &self.value
    }
}

// Uso
let c = Container::new(42);
```

**Zig:**

```zig
fn Container(comptime T: type) type {
    return struct {
        value: T,
        
        pub fn new(value: T) @This() {
            return .{ .value = value };
        }
        
        pub fn get(self: @This()) T {
            return self.value;
        }
    };
}

// Uso
const IntContainer = Container(i32);
var c = IntContainer.new(42);
```

**Padrão idiomático Zig:**

```zig
// Mais limpo e comum na prática
fn Container(comptime T: type) type {
    return struct {
        const Self = @This();
        value: T,
        
        pub fn new(value: T) Self {
            return .{ .value = value };
        }
        
        pub fn get(self: Self) T {
            return self.value;
        }
    };
}
```

### 3.3 Traits → ?

Rust tem traits para polimorfismo. Zig não tem equivalente direto. Aqui estão as alternativas:

**Opção 1: Duck typing com comptime (mais comum)**

```zig
// Não precisa de trait — só funciona se o tipo tiver o método
fn printDebug(value: anytype) void {
    // comptime verificação: o tipo tem método 'debug'?
    std.debug.print("{any}\n", .{value.debug()});
}
```

**Opção 2: Struct de funções (vtable manual)**

```zig
const Writer = struct {
    context: *anyopaque,
    writeFn: *const fn (*anyopaque, []const u8) anyerror!usize,
    
    pub fn write(self: Writer, bytes: []const u8) !usize {
        return self.writeFn(self.context, bytes);
    }
};

// Implementação para File
fn fileWrite(context: *anyopaque, bytes: []const u8) !usize {
    const file: *std.fs.File = @ptrCast(@alignCast(context));
    return file.write(bytes);
}
```

**Opção 3: Union de tipos conhecidos**

```zig
const Value = union(enum) {
    int: i64,
    float: f64,
    string: []const u8,
    
    pub fn format(self: Value) []const u8 {
        return switch (self) {
            .int => |i| std.fmt.allocPrint(allocator, "{d}", .{i}),
            .float => |f| std.fmt.allocPrint(allocator, "{d}", .{f}),
            .string => |s| s,
        };
    }
};
```

## 4. Collections

### 4.1 Vec<T> → ArrayList(T)

**Rust:**

```rust
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
vec.push(3);

for item in &vec {
    println!("{}", item);
}
```

**Zig:**

```zig
var list = std.ArrayList(i32).init(allocator);
defer list.deinit();

try list.append(1);
try list.append(2);
try list.append(3);

for (list.items) |item| {
    std.debug.print("{d}\n", .{item});
}
```

### 4.2 HashMap<K, V>

**Rust:**

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

let mut map = HashMap::new();
map.insert("key", "value");

if let Some(value) = map.get("key") {
    println!("{}", value);
}
```

**Zig:**

```zig
var map = std.StringHashMap([]const u8).init(allocator);
defer map.deinit();

try map.put("key", "value");

if (map.get("key")) |value| {
    std.debug.print("{s}\n", .{value});
}
```

## 5. Strings

### 5.1 String → []const u8 ou ArrayList(u8)

Em Zig, strings são simplesmente slices de bytes (`[]const u8`).

**Rust:**

```rust
let s1 = "string slice";           // &str
let s2 = String::from("owned");     // String
let s3 = format!("number: {}", 42); // String formatada
```

**Zig:**

```zig
const s1 = "string slice";         // []const u8
const s2 = try allocator.dupe(u8, "owned"); // []const u8 alocado
defer allocator.free(s2);

const s3 = try std.fmt.allocPrint(allocator, "number: {d}", .{42});
defer allocator.free(s3);

// String mutável (construção)
var builder = std.ArrayList(u8).init(allocator);
defer builder.deinit();
try builder.appendSlice("Hello");
try builder.appendSlice(", World!");
const result = builder.items; // []const u8
```

### 5.2 String Slices

**Rust:**

```rust
let s = "hello world";
let hello = &s[0..5]; // &str
let world = &s[6..11]; // &str
```

**Zig:**

```zig
const s = "hello world";
const hello = s[0..5]; // []const u8
const world = s[6..11]; // []const u8
```

**Nota:** Zig faz bounds checking em runtime em builds de debug (como Rust em modo debug).

## 6. Pattern Matching

### 6.1 match → switch

**Rust:**

```rust
match value {
    1 => println!("one"),
    2 => println!("two"),
    3..=10 => println!("three to ten"),
    _ => println!("other"),
}
```

**Zig:**

```zig
switch (value) {
    1 => std.debug.print("one\n", .{}),
    2 => std.debug.print("two\n", .{}),
    3...10 => std.debug.print("three to ten\n", .{}),
    else => std.debug.print("other\n", .{}),
}
```

### 6.2 if let → if com capture

**Rust:**

```rust
if let Some(value) = maybe_value {
    println!("{}", value);
}

if let Ok(result) = fallible_operation() {
    println!("{}", result);
}
```

**Zig:**

```zig
if (maybe_value) |value| {
    std.debug.print("{any}\n", .{value});
}

if (fallibleOperation()) |result| {
    std.debug.print("{any}\n", .{result});
} else |err| {
    std.debug.print("Error: {}\n", .{err});
}
```

### 6.3 while let → while com capture

**Rust:**

```rust
while let Some(item) = iterator.next() {
    println!("{}", item);
}
```

**Zig:**

```zig
while (iterator.next()) |item| {
    std.debug.print("{any}\n", .{item});
}
```

## 7. Option<T> → ?T

**Rust:**

```rust
fn find_user(id: u32) -> Option<User> {
    // ...
}

match find_user(42) {
    Some(user) => println!("{}", user.name),
    None => println!("User not found"),
}

// Ou com if let
if let Some(user) = find_user(42) {
    println!("{}", user.name);
}

// Ou com ?
let user = find_user(42)?;
```

**Zig:**

```zig
fn findUser(id: u32) ?User {
    // ...
}

if (findUser(42)) |user| {
    std.debug.print("{s}\n", .{user.name});
} else {
    std.debug.print("User not found\n", .{});
}

// Propaga null com 'try' em contexto opcional
const user = findUser(42) orelse return null;

// Orelse para default
const user = findUser(42) orelse User{ .name = "Unknown" };
```

## 8. Iteradores

### 8.1 Iterator → Slice ou While Loop

Zig não tem um sistema de iteradores tão rico quanto Rust. Você geralmente itera sobre slices diretamente.

**Rust:**

```rust
let sum: i32 = vec.iter()
    .filter(|x| **x > 0)
    .map(|x| x * 2)
    .sum();
```

**Zig:**

```zig
var sum: i32 = 0;
for (vec.items) |x| {
    if (x > 0) {
        sum += x * 2;
    }
}
```

Para operações mais complexas, você pode criar structs de iteração manualmente ou usar comptime para gerar código.

### 8.2 Iterator com Índice

**Rust:**

```rust
for (i, item) in vec.iter().enumerate() {
    println!("{}: {}", i, item);
}
```

**Zig:**

```zig
for (vec.items, 0..) |item, i| {
    std.debug.print("{d}: {any}\n", .{ i, item });
}
```

## 9. Async/Await

### 9.1 Conceitos Similares

A sintaxe é muito parecida, mas o modelo subjacente difere.

**Rust (com Tokio):**

```rust
async fn fetch_data(url: &str) -> Result<String, Error> {
    let response = reqwest::get(url).await?;
    let text = response.text().await?;
    Ok(text)
}

#[tokio::main]
async fn main() {
    let data = fetch_data("https://example.com").await.unwrap();
    println!("{}", data);
}
```

**Zig:**

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

fn fetchData(allocator: std.mem.Allocator, url: []const u8) ![]const u8 {
    var client = std.http.Client{ .allocator = allocator };
    defer client.deinit();
    
    const uri = try std.Uri.parse(url);
    
    var server_header_buffer: [4096]u8 = undefined;
    var request = try client.open(.GET, uri, .{
        .server_header_buffer = &server_header_buffer,
    });
    defer request.deinit();
    
    try request.send();
    try request.finish();
    try request.wait();
    
    return try request.reader().readAllAlloc(allocator, 1024 * 1024);
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    const data = try fetchData(allocator, "https://example.com");
    defer allocator.free(data);
    
    std.debug.print("{s}\n", .{data});
}
```

**Nota:** O Zig 0.13+ tem suporte a async/await mas é menos central na linguagem do que em Rust moderno. A std lib usa um modelo mais tradicional de callbacks/polling para I/O.

## 10. Build System: Cargo → build.zig

### 10.1 Comandos Comuns

| Cargo | Zig |
|-------|-----|
| `cargo new proj` | `mkdir proj && cd proj && zig init` |
| `cargo build` | `zig build` |
| `cargo run` | `zig build run` |
| `cargo test` | `zig build test` |
| `cargo add dep` | Edite `build.zig.zon` manualmente |
| `cargo publish` | Não há registry público ainda |

### 10.2 build.zig Básico

**Rust (Cargo.toml):**

```toml
[package]
name = "my-project"
version = "1.0.0"
edition = "2021"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
```

**Zig (build.zig):**

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

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    
    const exe = b.addExecutable(.{
        .name = "my-project",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    
    // Dependências via build.zig.zon são automaticamente disponíveis
    // const dep = b.dependency("nome_dep", .{});
    // exe.root_module.addImport("nome", dep.module("nome"));
    
    b.installArtifact(exe);
    
    const run_cmd = b.addRunArtifact(exe);
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
}
```

### 10.3 build.zig.zon (Dependências)

```zig
.{
    .name = "my-project",
    .version = "1.0.0",
    .dependencies = .{
        .known_folders = .{
            .url = "https://github.com/ziglibs/known-folders/archive/refs/tags/0.1.0.tar.gz",
            .hash = "...",
        },
    },
}
```

## 11. Testes

### 11.1 Testes Unitários

**Rust:**

```rust
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_add() {
        assert_eq!(add(2, 2), 4);
    }
    
    #[test]
    #[should_panic]
    fn test_panic() {
        panic!("expected");
    }
}
```

**Zig:**

```zig
fn add(a: i32, b: i32) i32 {
    return a + b;
}

test "add" {
    try std.testing.expectEqual(add(2, 2), 4);
}

test "panic" {
    // Para testar panics, use std.testing.expectError ou execute em subprocesso
}
```

### 11.2 Testes com Allocators

```zig
test "allocation" {
    const allocator = std.testing.allocator; // Detecta vazamentos automaticamente
    
    const slice = try allocator.alloc(u8, 100);
    defer allocator.free(slice); // Obrigatório, ou teste falha
    
    // Teste seus dados
    @memset(slice, 0);
    try std.testing.expectEqual(slice[0], 0);
} // Se houver vazamento, o teste falha
```

## 12. Macros → comptime

### 12.1 Derive Macros

**Rust:**

```rust
#[derive(Debug, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}
```

**Zig:**

Zig não tem derive automático. Você implementa manualmente ou gera com comptime:

```zig
const Point = struct {
    x: i32,
    y: i32,
    
    // Implemente manualmente
    pub fn format(
        self: Point,
        comptime fmt: []const u8,
        options: std.fmt.FormatOptions,
        writer: anytype,
    ) !void {
        _ = fmt;
        _ = options;
        try writer.print("Point{{ x: {d}, y: {d} }}", .{ self.x, self.y });
    }
};
```

### 12.2 Geração de Código com comptime

**Rust (macro_rules):**

```rust
macro_rules! vec_of_strings {
    ($($s:expr),*) => {
        vec![$($s.to_string()),*]
    };
}

let v = vec_of_strings!["a", "b", "c"];
```

**Zig (comptime):**

```zig
fn VecOfStrings(comptime strings: []const []const u8) type {
    return struct {
        const Self = @This();
        items: [strings.len][]const u8,
        
        pub fn init() Self {
            var result: Self = undefined;
            inline for (strings, 0..) |s, i| {
                result.items[i] = s;
            }
            return result;
        }
    };
}

const MyStrings = VecOfStrings(&.{"a", "b", "c"});
var v = MyStrings.init();
```

## Resumo: Guia Rápido de Tradução

| Conceito | Rust | Zig |
|----------|------|-----|
| Alocação heap | `Box`, `Vec` | `allocator.create`, `allocator.alloc`, `ArrayList` |
| Liberação automática | `Drop`, ownership | `defer`, `errdefer` |
| Erros | `Result<T, E>`, `Option<T>` | `T!E`, `?T` |
| Propagar erro | `?` | `try` |
| Null check | `if let`, `match` | `if (x) \|v\|`, `orelse` |
| Generics | `<T>` | `comptime T: type` |
| Traits | `trait` + `impl` | `comptime` duck typing, vtables manuais |
| Iteradores | `.iter()`, `.map()` | `for` em slices |
| Strings | `String`, `&str` | `[]const u8`, `ArrayList(u8)` |
| Módulos | `mod`, `pub` | `const`, `pub` |
| Testes | `#[test]` | `test "nome"` |
| Build | `Cargo.toml` | `build.zig` + `build.zig.zon` |

## Próximos Passos

1. 🔄 [Como Instalar o Zig](/tutoriais/como-instalar-zig/) — Configure seu ambiente
2. 📚 [Zig para Iniciantes](/tutoriais/zig-para-iniciantes/) — Revise conceitos básicos
3. ⚡ [Comptime em Zig](/tutoriais/comptime-em-zig/) — A feature mais poderosa do Zig
4. 🧪 [Testes em Zig](/tutoriais/testes-zig/) — Como testar código Zig efetivamente
5. 🆚 [Zig vs Rust: Qual Linguagem Escolher?](/artigos/zig-vs-rust/) — Comparação mais aprofundada

---

**Dúvidas específicas de migração?** A comunidade Zig é acolhedora e muitos membros vêm de Rust. Não hesite em perguntar!

*Última atualização: 10 de fevereiro de 2026*  
*Versão do Zig: 0.13.0*
