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
- Tratamento local de erros: Permite lidar com o erro no ponto exato onde ele ocorre.
- Valores de fallback: Fornece alternativas quando operações falham.
- Captura do erro: Permite inspecionar qual erro ocorreu para tomar decisões.
- Complementa
try: Enquantotrypropaga,catchtrata.
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
| Operador | Funciona com | Trata |
|---|---|---|
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
catchcom optional:catchfunciona apenas com error unions (!T). Para optionals (?T), useorelse. - 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