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
- Concisão: Elimina boilerplate de propagação de erros — a operação mais comum.
- Segurança: Impossível esquecer de tratar ou propagar um erro.
- Composição: Permite encadear operações que podem falhar de forma limpa.
- Clareza: Cada
trymarca 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
| Aspecto | try | catch |
|---|---|---|
| Propósito | Propagar erro | Tratar erro |
| Ação em erro | Retorna o erro | Executa código customizado |
| Resultado | Valor desembrulhado | Valor desembrulhado ou fallback |
| Quando usar | Não sabe lidar com o erro | Pode 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:
trysó funciona em funções que retornam!T. Usartryem uma funçãovoid(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:
trypropaga o erro “cru”. Se precisa adicionar contexação (ex: “falha ao conectar em porta 8080”), usecatchcom 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() voidnão pode usartry. Precisa serfn 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