Error Union em Zig — O que é e Como Usar

Error Union em Zig — O que é e Como Usar

Definição

Um Error Union em Zig é um tipo composto representado pelo operador ! que pode conter ou um valor válido ou um erro. É o mecanismo fundamental de tratamento de erros da linguagem, substituindo exceções (como em Java/Python) e códigos de retorno (como em C).

A sintaxe ErrorSet!T significa “este valor é do tipo T ou é um erro do conjunto ErrorSet”. Quando o error set é omitido (!T), o compilador infere o conjunto de erros automaticamente.

Por que Error Unions Importam

  1. Erros são valores: Em Zig, erros são cidadãos de primeira classe no sistema de tipos — não são exceções invisíveis.
  2. Impossível ignorar erros: O compilador obriga você a lidar com o caso de erro explicitamente.
  3. Zero custo: Error unions são implementados de forma eficiente, sem overhead de exceções.
  4. Composição: Funções que retornam error unions se compõem naturalmente com try.

Exemplo Prático

Retornando Error Unions

const std = @import("std");

const MathError = error{
    DivisaoPorZero,
    Overflow,
};

fn dividir(a: i32, b: i32) MathError!i32 {
    if (b == 0) return MathError.DivisaoPorZero;
    return @divTrunc(a, b);
}

pub fn main() void {
    // Tratando o erro explicitamente
    const resultado = dividir(10, 3) catch |err| {
        std.debug.print("Erro: {}\n", .{err});
        return;
    };
    std.debug.print("10 / 3 = {}\n", .{resultado});
}

Usando com try

fn processarDados() !void {
    const valor = try dividir(100, 5);  // Propaga erro automaticamente
    std.debug.print("Resultado: {}\n", .{valor});
}

try é equivalente a:

const valor = dividir(100, 5) catch |err| return err;

Usando com if

fn exemploIf() void {
    if (dividir(10, 0)) |valor| {
        std.debug.print("Sucesso: {}\n", .{valor});
    } else |err| {
        std.debug.print("Erro: {}\n", .{err});
    }
}

Error Union com tipo inferido

// O compilador infere o error set automaticamente
fn lerArquivo(caminho: []const u8) ![]const u8 {
    const arquivo = try std.fs.cwd().openFile(caminho, .{});
    defer arquivo.close();
    return try arquivo.readToEndAlloc(std.heap.page_allocator, 1024 * 1024);
}

Anatomia do Tipo

ErrorSet ! PayloadType
             
             └── Tipo do valor em caso de sucesso
   └── Conjunto de possíveis erros

Exemplos:
  error{OutOfMemory}![]u8      Pode ser OutOfMemory ou um slice de bytes
  anyerror!void                Qualquer erro ou void (sem valor)
  !u32                         Error set inferido, valor u32

Armadilhas Comuns

  • Usar anyerror desnecessariamente: Prefira error sets específicos. anyerror desabilita otimizações do compilador e dificulta a documentação da API.
  • Ignorar erros com catch unreachable: Só use quando tiver certeza absoluta de que o erro não pode ocorrer. Caso contrário, isso causará um panic em runtime.
  • Confundir !T com ?T: Error union (!T) carrega informação de erro; optional (?T) apenas indica presença/ausência de valor.
  • Não propagar erros: Se sua função não sabe lidar com o erro, propague-o com try em vez de engolir silenciosamente.

Termos Relacionados

  • Error Set — Conjuntos de erros nomeados
  • Try — Operador de propagação de erros
  • Catch — Operador para capturar erros
  • Optional — Tipo que pode ser null
  • Errdefer — Defer condicional para erros

Tutoriais Relacionados

Continue aprendendo Zig

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