Try in Non-Error Function — Como Resolver em Zig

Try in Non-Error Function — Como Resolver em Zig

O Que Este Erro Significa

O erro de compilação ocorre quando você usa a palavra-chave try dentro de uma função cujo tipo de retorno não é um error union. O operador try em Zig serve para propagar erros automaticamente — se a expressão resultar em erro, try retorna esse erro da função atual. Para isso funcionar, a função precisa ter um tipo de retorno que suporte erros (como !void, !u32, anyerror!T, etc.).

A mensagem do compilador:

error: expected type 'error union', found 'void'
note: cannot convert error union to non-error-union type

Ou:

error: 'try' is not allowed in function with return type 'void'

Causas Comuns

1. main() sem Tipo de Retorno de Erro

const std = @import("std");

pub fn main() void {  // void — não pode propagar erros
    const file = try std.fs.cwd().openFile("dados.txt", .{});
    // ERRO DE COMPILAÇÃO: 'try' em função void
    defer file.close();
}

2. Callback sem Tipo de Retorno de Erro

const std = @import("std");

fn meuCallback(item: u32) void {  // void — não aceita try
    const dados = try obterDados(item);
    // ERRO: try em função que retorna void
    _ = dados;
}

fn obterDados(id: u32) ![]u8 {
    _ = id;
    return error.NaoEncontrado;
}

3. Função de Comparação

const std = @import("std");

fn comparar(a: []const u8, b: []const u8) bool {
    const va = try std.fmt.parseInt(i32, a, 10);
    // ERRO: try em função que retorna bool
    const vb = try std.fmt.parseInt(i32, b, 10);
    return va < vb;
    _ = vb;
}

4. Método que Não Declara Erro no Retorno

const Processador = struct {
    fn processar(self: *Processador) void {  // void
        const resultado = try self.carregar();
        // ERRO: try em função void
        _ = resultado;
    }

    fn carregar(self: *Processador) ![]u8 {
        _ = self;
        return error.Falha;
    }
};

Como Corrigir

Solucao 1: Mudar o Tipo de Retorno para Error Union

A solução mais simples é permitir que a função propague erros:

const std = @import("std");

pub fn main() !void {  // Mudou de void para !void
    const file = try std.fs.cwd().openFile("dados.txt", .{});
    defer file.close();
    // Agora try funciona corretamente
}

Solucao 2: Usar catch ao Invés de try

Se a função não pode mudar seu tipo de retorno, trate o erro localmente:

const std = @import("std");

pub fn main() void {
    const file = std.fs.cwd().openFile("dados.txt", .{}) catch |err| {
        std.debug.print("Erro ao abrir arquivo: {}\n", .{err});
        return;
    };
    defer file.close();
    _ = file;
}

Solucao 3: catch com Valor Padrão

const std = @import("std");

fn comparar(context: void, a: []const u8, b: []const u8) bool {
    _ = context;
    const va = std.fmt.parseInt(i32, a, 10) catch return false;
    const vb = std.fmt.parseInt(i32, b, 10) catch return false;
    return va < vb;
}

Solucao 4: catch unreachable (Quando o Erro é Impossível)

const std = @import("std");

fn formatarNumero(valor: u32) void {
    var buf: [20]u8 = undefined;
    // bufPrint com u32 nunca falha quando o buffer é grande o suficiente
    const resultado = std.fmt.bufPrint(&buf, "{d}", .{valor}) catch unreachable;
    std.debug.print("{s}\n", .{resultado});
}

Solucao 5: Encapsular em Função que Retorna Erro

const std = @import("std");

fn meuCallbackInterno() !void {
    const dados = try obterDados(42);
    _ = dados;
}

fn meuCallback() void {
    meuCallbackInterno() catch |err| {
        std.debug.print("Erro no callback: {}\n", .{err});
    };
}

fn obterDados(id: u32) ![]u8 {
    _ = id;
    return error.NaoEncontrado;
}

Solucao 6: Usar if com Error Union

const std = @import("std");

fn processar(caminho: []const u8) void {
    if (std.fs.cwd().openFile(caminho, .{})) |file| {
        defer file.close();
        // Processa o arquivo
        _ = file;
    } else |err| {
        std.debug.print("Falha: {}\n", .{err});
    }
}

Quando Usar Cada Abordagem

SituaçãoAbordagem Recomendada
Função pode falharMudar retorno para !T + usar try
Callback com assinatura fixaUsar catch localmente
Erro é impossívelcatch unreachable (com cuidado)
Precisa de valor padrãocatch valor_padrao
Precisa log de errocatch |err| com bloco

Entendendo try vs catch

// try é açúcar sintático para:
const valor = try expressao;
// Equivale a:
const valor = expressao catch |err| return err;

// Portanto, try PRECISA que a função possa retornar erro
// Se não pode retornar erro, use catch diretamente

Padrão Comum: main com !void

Em Zig, é idiomático declarar main como !void para poder usar try:

const std = @import("std");

pub fn main() !void {
    // Agora podemos usar try livremente
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const dados = try allocator.alloc(u8, 1024);
    defer allocator.free(dados);

    const file = try std.fs.cwd().openFile("config.txt", .{});
    defer file.close();

    const bytes = try file.readAll(dados);
    std.debug.print("Lidos {} bytes\n", .{bytes});
}

Erros Relacionados

Continue aprendendo Zig

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