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ção | Abordagem Recomendada |
|---|---|
| Função pode falhar | Mudar retorno para !T + usar try |
| Callback com assinatura fixa | Usar catch localmente |
| Erro é impossível | catch unreachable (com cuidado) |
| Precisa de valor padrão | catch valor_padrao |
| Precisa log de erro | catch |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
- Error not handled — Erro não tratado
- Catch reached unreachable — Catch atingiu unreachable
- Expected return — Retorno esperado
- Attempt to unwrap error — Tentativa de desempacotar error