Error Not Handled — Como Resolver em Zig
O Que Este Erro Significa
O erro de compilação error is not handled ocorre quando uma função retorna um error union (!T) e o código que a chama ignora o possível erro. Zig exige que todos os erros sejam tratados explicitamente — não é possível simplesmente ignorá-los como em C, onde valores de retorno de erro podem ser descartados silenciosamente.
A mensagem de erro típica do compilador:
error: error is ignored
Ou:
error: error union is not handled
Esse design é intencional: Zig acredita que erros ignorados são a causa de muitos bugs em produção, e força o programador a tomar uma decisão consciente sobre cada erro possível.
Causas Comuns
1. Ignorar Retorno de Função que Pode Falhar
const std = @import("std");
pub fn main() void {
const file = std.fs.cwd().openFile("dados.txt", .{});
// ERRO DE COMPILAÇÃO: error union is not handled
_ = file;
}
2. Chamar Função Falhável sem try ou catch
const std = @import("std");
fn lerDados(allocator: std.mem.Allocator) ![]u8 {
return allocator.alloc(u8, 1024);
}
pub fn main() void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const dados = lerDados(allocator);
// ERRO: error union de lerDados não é tratado
_ = dados;
}
3. Usar Resultado de Error Union Diretamente
const std = @import("std");
fn dividir(a: u32, b: u32) !u32 {
if (b == 0) return error.DivisaoPorZero;
return a / b;
}
pub fn main() void {
const resultado = dividir(10, 2);
// ERRO: resultado é !u32, não u32
std.debug.print("{}\n", .{resultado});
}
4. Ignorar Erro em Expressão de Atribuição
const std = @import("std");
pub fn main() void {
var buf: [100]u8 = undefined;
const n = std.fmt.bufPrint(&buf, "Olá {}", .{"mundo"});
// ERRO: bufPrint retorna ![]u8
_ = n;
}
Como Corrigir
Solucao 1: Usar try para Propagar o Erro
A forma mais comum — propaga o erro para quem chamou a função:
const std = @import("std");
pub fn main() !void { // Note o !void
const file = try std.fs.cwd().openFile("dados.txt", .{});
defer file.close();
// Usa file normalmente
var buf: [1024]u8 = undefined;
const bytes = try file.read(&buf);
_ = bytes;
}
Solucao 2: Usar catch com Valor Padrão
const std = @import("std");
fn dividir(a: u32, b: u32) !u32 {
if (b == 0) return error.DivisaoPorZero;
return a / b;
}
pub fn main() void {
const resultado = dividir(10, 0) catch 0; // Se falhar, usa 0
std.debug.print("resultado: {}\n", .{resultado});
}
Solucao 3: Usar catch com Bloco de Tratamento
const std = @import("std");
pub fn main() void {
const file = std.fs.cwd().openFile("dados.txt", .{}) catch |err| {
std.debug.print("Não foi possível abrir arquivo: {}\n", .{err});
return;
};
defer file.close();
// Usa file...
_ = file;
}
Solucao 4: Usar catch com switch para Erros Específicos
const std = @import("std");
pub fn main() void {
const file = std.fs.cwd().openFile("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 file.close();
_ = file;
}
Solucao 5: Usar if com Error Union
const std = @import("std");
fn dividir(a: u32, b: u32) !u32 {
if (b == 0) return error.DivisaoPorZero;
return a / b;
}
pub fn main() void {
if (dividir(10, 2)) |resultado| {
std.debug.print("Sucesso: {}\n", .{resultado});
} else |err| {
std.debug.print("Erro: {}\n", .{err});
}
}
Solucao 6: Descartar Erro Explicitamente (Quando Apropriado)
Em casos raros onde você sabe que o erro não vai acontecer:
const std = @import("std");
pub fn main() void {
// Só use catch unreachable quando tiver CERTEZA de que não falhará
var buf: [100]u8 = undefined;
const resultado = std.fmt.bufPrint(&buf, "valor: {d}", .{@as(u32, 42)}) catch unreachable;
std.debug.print("{s}\n", .{resultado});
}
Padrões de Tratamento de Erro
Propagação com try
fn lerConfig(caminho: []const u8) !Config {
const file = try std.fs.cwd().openFile(caminho, .{});
defer file.close();
// try propaga qualquer erro para cima
const dados = try file.readToEndAlloc(allocator, max_size);
defer allocator.free(dados);
return try parseConfig(dados);
}
Fallback com catch
fn obterPorta() u16 {
const env_porta = std.process.getEnvVarOwned(allocator, "PORT") catch return 8080;
defer allocator.free(env_porta);
return std.fmt.parseInt(u16, env_porta, 10) catch 8080;
}
Conversão de Erro
const AppError = error{ConfigInvalida};
fn carregarApp() AppError!void {
lerConfig("app.conf") catch {
return AppError.ConfigInvalida;
};
}
Por Que Zig Exige Tratamento de Erros
Em muitas linguagens, erros são facilmente ignorados:
// C — erro ignorado silenciosamente
FILE *f = fopen("dados.txt", "r");
// Se f é NULL, o programa continua e crashará depois
Zig elimina essa classe de bugs ao tornar o tratamento de erros obrigatório. O compilador garante que toda possibilidade de falha é endereçada pelo programador, mesmo que a decisão seja “propagar para cima com try”.
Erros Relacionados
- Try in non-error function — Usar try em função que não retorna erro
- Catch reached unreachable — Catch atingiu unreachable
- Attempt to unwrap error — Tentar desempacotar error union
- Error payload not used — Payload de erro não utilizado