Undefined em Zig — O que é e Como Usar

Undefined em Zig — O que é e Como Usar

Definição

O valor undefined em Zig é um marcador especial que indica que uma variável não foi inicializada. Quando você atribui undefined a uma variável, está explicitamente dizendo ao compilador: “eu sei que este valor não tem conteúdo válido ainda, e me comprometo a inicializá-lo antes de usar”.

Em modo Debug e ReleaseSafe, variáveis undefined são preenchidas com 0xAA (padrão facilmente reconhecível em depuração). Em ReleaseFast e ReleaseSmall, undefined permite que o compilador otimize livremente, podendo resultar em qualquer valor.

Por que Undefined Importa

  1. Performance: Evita inicialização desnecessária de buffers grandes que serão preenchidos imediatamente.
  2. Explicitação: Torna claro que a não-inicialização é intencional, não um bug.
  3. Detecção de bugs: Em modo Debug, o padrão 0xAA ajuda a identificar uso acidental.
  4. Compatibilidade com C: Permite o mesmo padrão de “declara depois inicializa” comum em C.

Exemplo Prático

Uso com Buffers

const std = @import("std");

pub fn main() !void {
    // Buffer que será preenchido pela leitura
    var buffer: [1024]u8 = undefined;

    const stdin = std.io.getStdIn();
    const bytes_lidos = try stdin.read(&buffer);

    // Agora só a parte preenchida é válida
    const entrada = buffer[0..bytes_lidos];
    std.debug.print("Lido: {s}\n", .{entrada});
}

Uso com Arrays Calculados

fn calcularTabela() [256]u8 {
    var tabela: [256]u8 = undefined;
    for (0..256) |i| {
        tabela[i] = @intCast(i ^ 0xFF); // Preenche cada posição
    }
    return tabela;
}

const TABELA = calcularTabela();

Uso com Structs

const Conexao = struct {
    socket: std.posix.socket_t,
    buffer: [4096]u8,
    bytes_recebidos: usize,

    pub fn init(endereco: []const u8) !Conexao {
        var self: Conexao = undefined;
        self.socket = try conectar(endereco);
        self.bytes_recebidos = 0;
        // buffer será preenchido quando dados chegarem
        return self;
    }
};

Comparação: undefined vs Zero-init

// undefined — sem custo, mas perigoso se não inicializar
var buffer1: [1_000_000]u8 = undefined;

// Zero-init — seguro, mas gasta tempo zerando 1MB
var buffer2: [1_000_000]u8 = std.mem.zeroes([1_000_000]u8);

// Ou com @memset
var buffer3: [1_000_000]u8 = undefined;
@memset(&buffer3, 0);

O que Acontece em Cada Modo

ModoValor de undefinedVerificação
Debug0xAA repetidoSim (panic ao detectar uso)
ReleaseSafe0xAA repetidoSim
ReleaseFastLixo de memóriaNão
ReleaseSmallLixo de memóriaNão

Quando Usar undefined vs Alternativas

A decisão de usar undefined deve ser consciente. Aqui está um guia de quando cada abordagem faz sentido:

const std = @import("std");

// BOM: buffer que será 100% preenchido antes de ser lido
var buf_entrada: [512]u8 = undefined;
const n = try reader.read(&buf_entrada);
processar(buf_entrada[0..n]); // só a parte preenchida

// BOM: tabela calculada campo a campo em comptime
var tabela: [256]u32 = undefined;
for (0..256) |i| tabela[i] = @intCast(i * i);

// RUIM: struct onde alguns campos podem ficar sem inicialização
var config: Config = undefined;
config.porta = 8080;
// e config.host? Continua undefined — bug silencioso em Release

// MELHOR: inicializar com valor padrão e sobrescrever o necessário
var config = Config{
    .porta = 8080,
    .host = "localhost",
    .timeout = 30,
};

O Padrão 0xAA em Debug

Zig preenche memória undefined com 0xAA em modo Debug por uma razão específica: 0xAA em decimal é 170, e ponteiros com todos os bytes 0xAA (0xAAAAAAAA em 32 bits ou 0xAAAAAAAAAAAAAAAA em 64 bits) são endereços claramente inválidos — qualquer acesso causará uma falha de segmentação imediata em vez de corromper dados silenciosamente.

Além disso, em valores inteiros, 0xAA é um padrão incomum que raramente aparece em dados legítimos, tornando mais fácil identificar em um dump de memória que um campo não foi inicializado.

Comparação com Outras Linguagens

LinguagemVariáveis não inicializadasVerificação
Zigundefined explícito + 0xAA em DebugSim (Debug/Safe)
CValor de lixo silenciosoNão
RustProibido pelo compiladorCompile-time
GoZero-inicializado sempreN/A
C++Valor de lixo silenciosoNão (UB)

O Zig ocupa uma posição intermediária: exige que undefined seja explícito (ao contrário de C/C++, onde é silencioso), mas permite a otimização de não inicializar (ao contrário de Go, que sempre zera).

Armadilhas Comuns

  • Ler antes de inicializar: Em Release, undefined pode ser qualquer valor — ler sem inicializar é comportamento indefinido. Em Debug, causa panic.
  • Passar undefined para funções: Se uma função espera um valor válido e recebe undefined, o comportamento é indefinido.
  • Confundir com null: null é um valor válido para optionals; undefined significa “sem valor algum”.
  • Uso excessivo: Não use undefined como “default” para tudo. Se há um valor inicial razoável, use-o.
  • Retornar undefined: Retornar undefined de uma função é quase sempre um bug. O chamador esperará um valor válido.

Termos Relacionados

Tutoriais Relacionados

Continue aprendendo Zig

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