attempt to unwrap null optional — Como Resolver em Zig

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

Continue aprendendo Zig

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