attempt to unwrap null optional — Como Resolver em Zig
O Que Este Erro Significa
O erro attempt to unwrap null optional é um panic de runtime que ocorre quando você tenta acessar o valor interno de um tipo optional (?T) que é null. Em Zig, optionals são tipos que podem conter um valor do tipo T ou ser null. Tentar extrair o valor quando ele é null é um erro grave, similar ao NullPointerException em Java ou nil dereference em Go.
Esse panic geralmente acontece quando você usa .? (operador de unwrap) ou orelse unreachable em um optional que é null.
Causas Comuns
1. Usar .? em Optional Null
pub fn main() void {
const valor: ?u32 = null;
const x = valor.?; // PANIC: attempt to unwrap null optional
_ = x;
}
2. Função que Retorna null Inesperadamente
fn encontrar(arr: []const u32, alvo: u32) ?usize {
for (arr, 0..) |item, i| {
if (item == alvo) return i;
}
return null;
}
pub fn main() void {
const arr = [_]u32{ 1, 2, 3 };
const idx = encontrar(&arr, 99).?; // PANIC: 99 não está no array
_ = idx;
}
3. Acessar Ponteiro Optional Null
pub fn main() void {
var ptr: ?*u32 = null;
ptr.?.* = 42; // PANIC: ponteiro optional é null
}
4. Cadeia de Optionals
const Config = struct {
porta: ?u16,
};
fn carregarConfig() ?Config {
return null;
}
pub fn main() void {
const config = carregarConfig().?; // PANIC: config é null
_ = config;
}
5. Optional em Struct Não Inicializado
const Usuario = struct {
nome: []const u8,
email: ?[]const u8 = null,
};
pub fn main() void {
const user = Usuario{ .nome = "Ana" };
const email = user.email.?; // PANIC: email não foi definido (é null)
_ = email;
}
Como Corrigir
Solução 1: Usar orelse com Valor Padrão
A forma mais segura de lidar com optionals:
pub fn main() void {
const valor: ?u32 = null;
const x = valor orelse 0; // Se null, usa 0
_ = x; // x == 0
}
Solução 2: Usar if Para Verificar
const std = @import("std");
pub fn main() void {
const valor: ?u32 = null;
if (valor) |v| {
std.debug.print("valor: {}\n", .{v});
} else {
std.debug.print("valor é null\n", .{});
}
}
Solução 3: Usar orelse return/break/continue
fn processar(dados: ?[]const u8) void {
const d = dados orelse return; // Retorna se null
// Daqui em diante, 'd' é garantidamente não-null
_ = d;
}
Solução 4: Usar orelse com @panic Para Mensagem Customizada
Se o null realmente não deveria acontecer:
pub fn main() void {
const config = carregarConfigObrigatoria();
const porta = config.porta orelse @panic("Porta não configurada!");
_ = porta;
}
Solução 5: Verificar Antes de Desempacotar
const std = @import("std");
fn encontrar(arr: []const u32, alvo: u32) ?usize {
for (arr, 0..) |item, i| {
if (item == alvo) return i;
}
return null;
}
pub fn main() void {
const arr = [_]u32{ 1, 2, 3 };
if (encontrar(&arr, 99)) |idx| {
std.debug.print("Encontrado no índice: {}\n", .{idx});
} else {
std.debug.print("Não encontrado\n", .{});
}
}
Solução 6: Converter Optional em Error Union
const BuscaError = error{NaoEncontrado};
fn encontrarOuErro(arr: []const u32, alvo: u32) BuscaError!usize {
for (arr, 0..) |item, i| {
if (item == alvo) return i;
}
return BuscaError.NaoEncontrado;
}
Padrões Seguros com Optionals
Guard Clause com orelse return
fn processar(config: ?Config) !void {
const cfg = config orelse return error.ConfigNula;
// Usa cfg com segurança
_ = cfg;
}
Encadeamento Seguro
fn obterPorta(config: ?Config) u16 {
const cfg = config orelse return 8080;
const porta = cfg.porta orelse return 8080;
return porta;
}
Map/Transform Optional
fn formatarEmail(email: ?[]const u8) []const u8 {
return email orelse "(sem email)";
}
Quando .? É Aceitável
Use .? apenas quando você tem certeza absoluta de que o valor não é null:
fn processar(lista: []const ?u32) void {
for (lista) |item| {
if (item != null) {
// Aqui sabemos que item não é null
const valor = item.?; // Seguro neste contexto
_ = valor;
}
}
}
Mas mesmo nesses casos, if (item) |valor| é mais idiomático.
Erros Relacionados
- attempt to unwrap error — Tentativa de desempacotar error union
- error not handled — Erro não tratado
- catch reached unreachable — Catch atingiu unreachable