@panic em Zig — Referência e Exemplos

@panic em Zig

O @panic encerra a execução do programa imediatamente com uma mensagem de erro e, em modo debug, um stack trace. É usado para situações irrecuperáveis onde continuar a execução seria inseguro ou impossível. O tipo de retorno é noreturn — o compilador sabe que o fluxo nunca continua após um panic.

Sintaxe

@panic(message: []const u8) noreturn

Parâmetros

  • message ([]const u8): Mensagem de erro exibida ao encerrar o programa.

Valor de retorno

noreturn — a função nunca retorna. O programa é encerrado.

Exemplos práticos

Exemplo 1: Panic em caso de estado inválido

const std = @import("std");

const Estado = enum { ativo, pausado, finalizado };

fn processar(estado: Estado) void {
    switch (estado) {
        .ativo => std.debug.print("Processando...\n", .{}),
        .pausado => std.debug.print("Em pausa.\n", .{}),
        .finalizado => @panic("Tentativa de processar estado finalizado"),
    }
}

pub fn main() void {
    processar(.ativo);
    // processar(.finalizado); // Causaria panic!
}

Exemplo 2: Coerção noreturn em expressões

const std = @import("std");

fn buscarConfig(chave: []const u8) []const u8 {
    const configs = [_]struct { k: []const u8, v: []const u8 }{
        .{ .k = "host", .v = "localhost" },
        .{ .k = "porta", .v = "8080" },
    };

    for (configs) |c| {
        if (std.mem.eql(u8, c.k, chave)) return c.v;
    }

    // @panic tem tipo noreturn, coercível para []const u8
    @panic("Configuração obrigatória não encontrada");
}

pub fn main() void {
    const host = buscarConfig("host");
    std.debug.print("Host: {s}\n", .{host});
}

Exemplo 3: Validação de pré-condições

const std = @import("std");

const MatrizQuadrada = struct {
    dados: []f64,
    tamanho: usize,

    pub fn init(dados: []f64) MatrizQuadrada {
        const n = std.math.sqrt(@as(f64, @floatFromInt(dados.len)));
        const tamanho: usize = @intFromFloat(n);

        if (tamanho * tamanho != dados.len) {
            @panic("Dados não formam uma matriz quadrada");
        }

        return .{ .dados = dados, .tamanho = tamanho };
    }

    pub fn get(self: MatrizQuadrada, linha: usize, coluna: usize) f64 {
        if (linha >= self.tamanho or coluna >= self.tamanho) {
            @panic("Índice fora dos limites da matriz");
        }
        return self.dados[linha * self.tamanho + coluna];
    }
};

Quando usar @panic

  1. Invariantes violados: Quando uma condição que deveria ser impossível ocorre.
  2. Configuração ausente: Recursos obrigatórios que não podem ter fallback.
  3. Bugs: Situações que indicam bug no código, não erro do usuário.
  4. Protótipos: Placeholder temporário para lógica não implementada.

Quando NÃO usar @panic

  • Para erros esperados (use error unions e try/catch).
  • Para validação de entrada do usuário (retorne erro).
  • Para condições que podem ser tratadas graciosamente.

Comportamento por modo de compilação

O comportamento de @panic varia conforme o modo de otimização escolhido:

  • Debug e ReleaseSafe: Imprime a mensagem de panic e um stack trace completo no stderr, depois encerra com código de saída não-zero.
  • ReleaseFast e ReleaseSmall: O stack trace pode ser omitido para reduzir tamanho do binário, mas a mensagem ainda é exibida.
  • Freestanding (bare metal): O panic chama a função panic definida no root module. Pode ser customizado para piscar um LED de erro, reiniciar o sistema, etc.

Customizando o handler de panic

É possível substituir o comportamento padrão de panic definindo uma função panic no arquivo raiz do projeto:

// main.zig ou root.zig
const std = @import("std");

pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
    _ = stack_trace;
    _ = ret_addr;
    std.log.err("PANIC CUSTOMIZADO: {s}", .{message});
    // Em embarcados, você pode reiniciar o sistema aqui
    std.process.exit(1);
}

Isso é especialmente útil em sistemas embarcados, onde o comportamento padrão de imprimir stack trace não é viável.

Comparação com equivalente em C

Em C, o equivalente mais próximo é assert() ou abort():

#include <assert.h>
#include <stdlib.h>

// C: assert (desativado em release com -DNDEBUG)
assert(x > 0);

// C: abort (sempre ativo, mas sem mensagem)
if (x > 0) abort();

// C: fprintf + abort (mais próximo do @panic)
if (!(x > 0)) {
    fprintf(stderr, "x deve ser positivo\n");
    abort();
}

Em Zig, @panic é sempre ativo (não pode ser desativado por flag de compilação), fornece mensagem clara e integra com o sistema de stack trace do runtime.

Erros comuns

1. Usar @panic para erros recuperáveis:

// ERRADO: arquivo inexistente não é bug, é condição esperada
fn lerArquivo(caminho: []const u8) []u8 {
    const arquivo = std.fs.cwd().openFile(caminho, .{}) catch
        @panic("arquivo não encontrado");
    // ...
}

// CORRETO: propagar o erro para o chamador decidir
fn lerArquivo(caminho: []const u8) ![]u8 {
    const arquivo = try std.fs.cwd().openFile(caminho, .{});
    // ...
}

2. Usar @panic com string formatada@panic aceita apenas strings estáticas. Para mensagens dinâmicas, combine com std.debug.panic:

// ERRADO: @panic não formata strings
// @panic(std.fmt.allocPrint(...)); // não funciona assim

// CORRETO: usar std.debug.panic para mensagens formatadas
std.debug.panic("Valor inválido: {}", .{valor});

Perguntas Frequentes

P: Qual é a diferença entre @panic e unreachable?

R: unreachable é uma asserção de que um ponto do código nunca será alcançado. Em modo Debug, gera panic com mensagem genérica “reached unreachable code”. Em ReleaseFast, torna-se undefined behavior (sem verificação). @panic sempre termina com a mensagem fornecida, em qualquer modo.

P: @panic pode ser chamado em comptime?

R: Não diretamente. Em comptime, use @compileError para sinalizar erros. @panic é exclusivamente runtime.

P: Como capturar um panic nos testes?

R: O framework de testes do Zig trata panics como falhas de teste. Não há como “capturar” um panic de forma controlada — a filosofia do Zig é que panics indicam bugs que devem ser corrigidos, não tratados.

Builtins relacionados

  • @compileError — Erro em tempo de compilação (não runtime)
  • @src — Informações de localização para debugging
  • @compileLog — Debug em tempo de compilação

Tutoriais relacionados

Continue aprendendo Zig

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