Try em Zig — O que é e Como Usar

Try em Zig — O que é e Como Usar

Definição

O operador try em Zig é um atalho para propagar erros automaticamente. Quando aplicado a um error union, try desembrulha o valor de sucesso ou retorna imediatamente o erro para a função chamadora. É o equivalente do operador ? de Rust.

try x é açúcar sintático para: x catch |err| return err.

Por que Try Importa

  1. Concisão: Elimina boilerplate de propagação de erros — a operação mais comum.
  2. Segurança: Impossível esquecer de tratar ou propagar um erro.
  3. Composição: Permite encadear operações que podem falhar de forma limpa.
  4. Clareza: Cada try marca explicitamente um ponto de possível falha.

Exemplo Prático

Uso Básico

const std = @import("std");

fn lerArquivo(caminho: []const u8) ![]u8 {
    const allocator = std.heap.page_allocator;

    // Cada try pode falhar e propagar o erro
    const arquivo = try std.fs.cwd().openFile(caminho, .{});
    defer arquivo.close();

    const conteudo = try arquivo.readToEndAlloc(allocator, 1024 * 1024);
    return conteudo;
}

pub fn main() !void {
    const dados = try lerArquivo("exemplo.txt");
    defer std.heap.page_allocator.free(dados);

    std.debug.print("Lido: {} bytes\n", .{dados.len});
}

O que Try Faz Internamente

// Estas duas formas são equivalentes:

// Com try:
const valor = try operacaoQuePoedeFalhar();

// Sem try (expandido):
const valor = operacaoQuePoedeFalhar() catch |err| return err;

Encadeando Múltiplos Trys

fn processarPedido(allocator: std.mem.Allocator) !Pedido {
    const config = try carregarConfig(allocator);
    defer allocator.free(config);

    const conexao = try conectarBanco(config);
    defer conexao.close();

    const dados = try conexao.query("SELECT * FROM pedidos LIMIT 1");
    defer allocator.free(dados);

    const pedido = try Pedido.parse(dados);
    return pedido;
}

Se qualquer try falhar, a função retorna imediatamente com o erro, e os defers já executados garantem a limpeza dos recursos.

Try com Errdefer

fn inicializar(allocator: std.mem.Allocator) !*App {
    const app = try allocator.create(App);
    errdefer allocator.destroy(app);

    app.config = try carregarConfig();
    errdefer app.config.deinit();

    app.db = try conectarDB(app.config);
    // Se chegar aqui, tudo deu certo

    return app;
}

Try em Main

// main pode retornar erro — o runtime do Zig trata
pub fn main() !void {
    try executar();
}
// Se executar() falhar, a mensagem de erro é impressa automaticamente

Try vs Catch

Aspectotrycatch
PropósitoPropagar erroTratar erro
Ação em erroRetorna o erroExecuta código customizado
ResultadoValor desembrulhadoValor desembrulhado ou fallback
Quando usarNão sabe lidar com o erroPode recuperar do erro
// try: propaga para quem chamou
const a = try operacao();

// catch: trata localmente
const b = operacao() catch |err| {
    log.err("Falhou: {}", .{err});
    return valor_padrao;
};

Armadilhas Comuns

  • Try fora de função com error union: try só funciona em funções que retornam !T. Usar try em uma função void (sem !) causa erro de compilação.
  • Usar try onde catch é melhor: Se você pode se recuperar do erro, use catch. try é para quando o erro deve subir.
  • Perda de contexto: try propaga o erro “cru”. Se precisa adicionar contexação (ex: “falha ao conectar em porta 8080”), use catch com um novo erro.
  • Muitos trys seguidos: Se uma função tem dezenas de try, considere se ela está fazendo coisas demais.
  • Esquecer ! no retorno: fn foo() void não pode usar try. Precisa ser fn foo() !void.

Termos Relacionados

  • Error Union — Tipo que carrega valor ou erro
  • Catch — Tratamento local de erros
  • Errdefer — Limpeza condicional em caso de erro
  • Error Set — Conjuntos de erros tipados
  • Defer — Limpeza garantida ao sair do escopo

Tutoriais Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.