Introdução
Error sets em Zig permitem definir conjuntos específicos de erros que uma função pode retornar. Isso dá ao chamador informação precisa sobre quais erros esperar, sem a necessidade de hierarquias de exceções como em Java ou C#.
Para padrões de uso com try/catch, veja Padrões Try/Catch. Para cleanup, consulte Padrões Errdefer.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
Definir Error Sets
Error Set Simples
const ArquivoError = error{
NaoEncontrado,
SemPermissao,
Corrompido,
DiskCheio,
};
fn abrirArquivo(caminho: []const u8) ArquivoError!std.fs.File {
_ = caminho;
return error.NaoEncontrado;
}
Error Set com Documentação
const ParseError = error{
/// Token inesperado encontrado durante parsing
TokenInesperado,
/// Fim de input antes do esperado
FimPrematuro,
/// Valor numérico fora do intervalo válido
ValorForaDoIntervalo,
/// Encoding de caractere inválido
EncodingInvalido,
};
Composição de Error Sets
Unir Error Sets
const IoError = error{
ArquivoNaoEncontrado,
PermissaoNegada,
Timeout,
};
const ParseError = error{
SintaxeInvalida,
TokenInesperado,
};
// Combinar dois error sets
const ConfigError = IoError || ParseError;
fn carregarConfig(caminho: []const u8) ConfigError!Config {
const dados = lerArquivo(caminho) catch |err| return err;
return parsear(dados) catch |err| return err;
}
Error Set Implícito com !
// ! sem tipo explícito: error set inferido pelo compilador
fn processar(dados: []const u8) !Resultado {
// O compilador infere automaticamente quais erros são possíveis
const arquivo = try std.fs.cwd().openFile(dados, .{});
defer arquivo.close();
// ...
}
Usar Error Sets em Switch
const DbError = error{
ConexaoRecusada,
QueryInvalida,
RegistroNaoEncontrado,
ConstraintViolada,
Timeout,
};
fn executarQuery(query: []const u8) DbError!Resultado {
_ = query;
return error.Timeout;
}
fn tratarErroDb(err: DbError) void {
switch (err) {
error.ConexaoRecusada => {
std.debug.print("Servidor não disponível. Tente novamente.\n", .{});
},
error.QueryInvalida => {
std.debug.print("Query SQL inválida.\n", .{});
},
error.RegistroNaoEncontrado => {
std.debug.print("Registro não encontrado.\n", .{});
},
error.ConstraintViolada => {
std.debug.print("Violação de constraint.\n", .{});
},
error.Timeout => {
std.debug.print("Operação expirou.\n", .{});
},
}
}
Error Sets em Structs
const HttpClient = struct {
pub const Error = error{
ConexaoRecusada,
Timeout,
SslHandshakeFailed,
RespostaInvalida,
RedirectLoop,
};
pub fn get(self: *HttpClient, url: []const u8) Error!Resposta {
_ = self;
_ = url;
return error.Timeout;
}
pub fn post(self: *HttpClient, url: []const u8, body: []const u8) Error!Resposta {
_ = self;
_ = url;
_ = body;
return error.ConexaoRecusada;
}
};
Converter entre Error Sets
const MeuErro = error{
Generico,
Especifico,
};
fn mapearErro(err: anyerror) MeuErro {
return switch (err) {
error.FileNotFound => error.Especifico,
error.AccessDenied => error.Especifico,
else => error.Generico,
};
}
Error Set como Valor
const std = @import("std");
fn logErro(err: anyerror) void {
std.debug.print("Erro: {s}\n", .{@errorName(err)});
}
fn transformarErro(err: anyerror) !void {
std.debug.print("Erro original: {s}\n", .{@errorName(err)});
return err;
}
Boas Práticas
1. Nomeie erros descritivamente
// BOM
const Error = error{BufferCheio, ConexaoInterrompida, FormatoInvalido};
// RUIM
const Error = error{Err1, Err2, Err3};
2. Use error sets específicos em APIs públicas
// BOM: chamador sabe exatamente o que pode acontecer
pub fn conectar(host: []const u8) error{DnsNaoResolvido, ConexaoRecusada, Timeout}!Conexao {
// ...
}
// Aceitável para funções internas: ! inferido
fn processarInterno(dados: []const u8) !Resultado {
// ...
}
3. Componha error sets para funções que chamam múltiplas sub-funções
pub const ProcessarError = IoError || ParseError || ValidacaoError;
Testes
const std = @import("std");
test "erro específico" {
const resultado = abrirArquivo("inexistente.txt");
try std.testing.expectError(error.NaoEncontrado, resultado);
}
test "sucesso" {
const resultado = try abrirArquivo("existente.txt");
_ = resultado;
}
Veja Testes Unitários e Test Expectations.
Conclusão
Error sets em Zig são tipados, composíveis e verificados pelo compilador. Eles oferecem toda a informação necessária para tratamento de erros sem a complexidade de hierarquias de exceções.
Para padrões relacionados, veja Padrões Try/Catch, Padrões Errdefer e Error Logging.