Error Not Handled — Como Resolver em Zig

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

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.