@errorName em Zig
O @errorName retorna o nome de um valor de erro como uma string legível. É uma ferramenta essencial para depuração, logging e mensagens de erro amigáveis. Em Zig, erros são valores de primeira classe, e este builtin permite converter esses valores para suas representações textuais.
Sintaxe
@errorName(err: anyerror) [:0]const u8
O que faz
O @errorName recebe um valor de erro e retorna seu nome como uma string de bytes constante terminada em sentinela nulo. O nome retornado corresponde exatamente ao identificador usado na declaração do error set.
Parâmetros
- err (
anyerror): Um valor de erro de qualquer error set. Deve ser um erro concreto, não um error union (!T).
Valor de retorno
Retorna [:0]const u8 — uma string terminada em zero contendo o nome do erro.
Exemplos práticos
Exemplo 1: Logging de erros
const std = @import("std");
const ArquivoErro = error{
NaoEncontrado,
PermissaoNegada,
DiskCheio,
Corrompido,
};
fn abrirArquivo(caminho: []const u8) ArquivoErro!void {
_ = caminho;
return ArquivoErro.NaoEncontrado;
}
test "logging de erros" {
const resultado = abrirArquivo("/tmp/teste.txt");
if (resultado) |_| {
// Sucesso
} else |err| {
const nome = @errorName(err);
std.debug.print("Erro ao abrir arquivo: {s}\n", .{nome});
// Saída: "Erro ao abrir arquivo: NaoEncontrado"
try std.testing.expectEqualStrings("NaoEncontrado", nome);
}
}
Exemplo 2: Sistema de logging estruturado
const std = @import("std");
const NivelLog = enum { debug, info, warn, err };
fn log(nivel: NivelLog, mensagem: []const u8, erro_opt: ?anyerror) void {
const prefixo = switch (nivel) {
.debug => "[DEBUG]",
.info => "[INFO]",
.warn => "[WARN]",
.err => "[ERRO]",
};
if (erro_opt) |err| {
std.debug.print("{s} {s} (erro: {s})\n", .{
prefixo,
mensagem,
@errorName(err),
});
} else {
std.debug.print("{s} {s}\n", .{ prefixo, mensagem });
}
}
test "logging estruturado" {
log(.info, "Servidor iniciado", null);
log(.err, "Falha na conexão", error.ConnectionRefused);
log(.warn, "Tentativa de acesso negada", error.PermissaoNegada);
}
Exemplo 3: Tradução de erros para mensagens amigáveis
const std = @import("std");
const DbErro = error{
ConexaoPerdida,
TabelaNaoExiste,
ChaveDuplicada,
TimeoutConsulta,
};
fn mensagemAmigavel(err: DbErro) []const u8 {
return switch (err) {
error.ConexaoPerdida => "A conexão com o banco de dados foi perdida. Tente novamente.",
error.TabelaNaoExiste => "A tabela solicitada não foi encontrada no banco de dados.",
error.ChaveDuplicada => "Já existe um registro com essa chave. Use um valor diferente.",
error.TimeoutConsulta => "A consulta demorou demais. Tente uma consulta mais simples.",
};
}
fn tratarErroDb(err: DbErro) void {
// Mensagem técnica para logs
std.debug.print("DB Error: {s}\n", .{@errorName(err)});
// Mensagem amigável para o usuário
std.debug.print("Mensagem: {s}\n", .{mensagemAmigavel(err)});
}
test "mensagens de erro" {
tratarErroDb(error.ConexaoPerdida);
tratarErroDb(error.ChaveDuplicada);
}
Casos de uso comuns
- Logging e monitoramento: Registrar o nome do erro em logs para diagnóstico e análise de incidentes.
- Mensagens de erro para o usuário: Exibir o nome técnico do erro junto com uma mensagem amigável.
- Serialização de erros: Converter erros para strings ao transmitir via rede (APIs, RPC).
- Depuração: Imprimir erros durante o desenvolvimento para entender fluxos de execução.
- Telemetria: Enviar nomes de erros para sistemas de monitoramento e alertas.
Observações
- O
@errorNamefunciona com qualquer error set, incluindoanyerror. - A string retornada é estática e válida durante toda a execução do programa.
- Não há operação inversa direta (string para erro) disponível como builtin. Para isso, seria necessário implementar manualmente usando
@typeInfo.
Comparação com C equivalente
Em C, não existe um mecanismo nativo equivalente. Desenvolvedores costumam usar strerror() para erros do sistema ou criar tabelas manuais:
// C — tabela manual de mensagens de erro
const char* meu_strerror(int codigo) {
switch (codigo) {
case ERR_NAO_ENCONTRADO: return "NaoEncontrado";
case ERR_PERMISSAO: return "PermissaoNegada";
default: return "Desconhecido";
}
}
Em Zig, o compilador mantém automaticamente os nomes de todos os erros declarados, e @errorName os recupera sem nenhuma tabela manual:
// Zig — automático, sem manutenção
const nome = @errorName(err); // sempre correto, sempre atualizado
Isso elimina a possibilidade de a tabela ficar desatualizada quando novos erros são adicionados.
Detalhes técnicos
Os nomes retornados por @errorName são armazenados em uma seção de dados somente-leitura do binário gerado. O compilador coleta todos os valores de erro usados no programa e cria uma tabela de strings em tempo de compilação. Por isso, a chamada a @errorName em runtime é basicamente uma busca por índice em uma tabela — extremamente eficiente.
O tipo de retorno [:0]const u8 indica uma slice de bytes com sentinela nulo no final, o que a torna compatível com APIs C que esperam const char*. Isso é conveniente ao integrar logs Zig com sistemas de logging C.
Erros comuns
Erro: Tentar chamar @errorName em um error union sem primeiro capturar o erro.
// ERRADO — resultado é !void, não anyerror
const resultado = funcaoQueRetornaErro();
// @errorName(resultado) — não compila!
// CORRETO — capturar o erro primeiro
if (resultado) |_| {} else |err| {
std.debug.print("{s}\n", .{@errorName(err)});
}
Perguntas Frequentes
P: O @errorName tem custo em performance?
É extremamente eficiente. O nome já está armazenado como string estática no binário. A chamada em runtime é essencialmente uma leitura de ponteiro de uma tabela gerada pelo compilador.
P: Como converter um nome de string de volta para um valor de erro?
Não existe builtin para isso. É preciso iterar sobre os erros do error set usando @typeInfo e comparar as strings manualmente. Para casos simples, um switch em string é mais direto:
fn stringParaErro(nome: []const u8) ?anyerror {
if (std.mem.eql(u8, nome, "NaoEncontrado")) return error.NaoEncontrado;
if (std.mem.eql(u8, nome, "PermissaoNegada")) return error.PermissaoNegada;
return null;
}
P: @errorName funciona com anyerror?
Sim. O anyerror é o supertipo de todos os erros em Zig, e @errorName funciona com qualquer valor de erro, independentemente do error set específico.
Builtins relacionados
- @tagName — Obtém o nome do campo ativo de uma tagged union
- @typeName — Obtém o nome de um tipo como string
- @typeInfo — Inspeciona error sets e outros tipos
- @compileError — Gera erro de compilação personalizado