Catch em Zig — O que é e Como Usar

Catch em Zig — O que é e Como Usar

Definição

O operador catch em Zig é usado para capturar e tratar erros de error unions (ErrorSet!T). Ele fornece um mecanismo para reagir quando uma operação falha, seja fornecendo um valor de fallback, executando lógica de recuperação ou propagando o erro de forma diferente.

A sintaxe básica é: error_union catch fallback ou error_union catch |err| { ... }.

Por que Catch Importa

  1. Tratamento local de erros: Permite lidar com o erro no ponto exato onde ele ocorre.
  2. Valores de fallback: Fornece alternativas quando operações falham.
  3. Captura do erro: Permite inspecionar qual erro ocorreu para tomar decisões.
  4. Complementa try: Enquanto try propaga, catch trata.

Exemplo Prático

Catch com Valor de Fallback

const std = @import("std");

fn parsearNumero(texto: []const u8) !u32 {
    return std.fmt.parseInt(u32, texto, 10);
}

pub fn main() void {
    // Se falhar, usa 0 como padrão
    const valor = parsearNumero("42") catch 0;
    std.debug.print("Valor: {}\n", .{valor}); // 42

    const invalido = parsearNumero("abc") catch 0;
    std.debug.print("Inválido: {}\n", .{invalido}); // 0
}

Catch com Captura de Erro

const std = @import("std");

fn abrirArquivo(caminho: []const u8) !std.fs.File {
    return std.fs.cwd().openFile(caminho, .{});
}

pub fn main() void {
    const arquivo = abrirArquivo("config.txt") catch |err| {
        switch (err) {
            error.FileNotFound => {
                std.debug.print("Arquivo não encontrado, usando padrões\n", .{});
                return;
            },
            error.AccessDenied => {
                std.debug.print("Permissão negada\n", .{});
                return;
            },
            else => {
                std.debug.print("Erro inesperado: {}\n", .{err});
                return;
            },
        }
    };
    defer arquivo.close();

    std.debug.print("Arquivo aberto com sucesso\n", .{});
}

Catch com Bloco e Lógica

const porta = parsearNumero(config.porta_texto) catch blk: {
    std.log.warn("Porta inválida, usando 8080", .{});
    metricas.incrementar("config_fallback");
    break :blk 8080;
};

Catch vs Try

fn processar() !void {
    // try: propaga o erro para o chamador
    const a = try operacao1();

    // catch: trata o erro localmente
    const b = operacao2() catch |err| {
        std.log.err("operacao2 falhou: {}", .{err});
        return err; // Pode re-propagar manualmente
    };

    _ = a;
    _ = b;
}

Catch com Unreachable

// Quando sabemos que o erro é impossível
const valor: u32 = std.fmt.parseInt(u32, "42", 10) catch unreachable;

Padrão: Logging + Fallback

fn obterConfiguracao() Config {
    return lerConfigDoArquivo() catch |err| {
        std.log.warn("Falha ao ler config ({s}), usando padrão", .{@errorName(err)});
        return Config.padrao();
    };
}

Catch vs Orelse

OperadorFunciona comTrata
catch!T (error union)Erros
orelse?T (optional)null
// catch para error unions
const a: anyerror!u32 = error.Falha;
const val_a = a catch 0;

// orelse para optionals
const b: ?u32 = null;
const val_b = b orelse 0;

Armadilhas Comuns

  • Usar catch com optional: catch funciona apenas com error unions (!T). Para optionals (?T), use orelse.
  • Catch com unreachable irresponsável: Se o erro realmente ocorrer em Release, é comportamento indefinido. Use apenas quando tiver certeza.
  • Engolir erros silenciosamente: operacao() catch {} ignora o erro sem tratamento. Pelo menos registre um log.
  • Não usar a informação do erro: Quando possível, capture o erro com catch |err| para logging ou decisões.

Termos Relacionados

  • Error Union — Tipo que pode conter erro ou valor
  • Try — Propagação automática de erros
  • Orelse — Equivalente para optionals
  • Error Set — Conjuntos de erros nomeados
  • Errdefer — Limpeza condicional em caso de erro

Tutoriais Relacionados

Continue aprendendo Zig

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