noreturn em Zig — O que é e Como Usar

noreturn em Zig — O que é e Como Usar

Definição

noreturn é um tipo especial em Zig que indica que uma expressão ou função nunca retorna ao chamador. Quando uma função tem tipo de retorno noreturn, o compilador sabe que o fluxo de execução nunca continuará após a chamada. Isso permite otimizações e garante que código morto após chamadas noreturn seja detectado.

Exemplos de expressões noreturn incluem @panic(), std.process.exit(), unreachable e loops infinitos (while (true) {}).

Por que noreturn Importa

  1. Otimização: O compilador pode eliminar código após chamadas noreturn.
  2. Correção: Branches em switch/if que terminam com noreturn não precisam produzir valor.
  3. Coerção: noreturn pode ser coagido para qualquer tipo, permitindo uso flexível em expressões.
  4. Documentação: Deixa explícito que uma função encerra o programa ou nunca termina.

Exemplo Prático

Função que Nunca Retorna

const std = @import("std");

fn falha_fatal(mensagem: []const u8) noreturn {
    std.debug.print("ERRO FATAL: {s}\n", .{mensagem});
    std.process.exit(1);
}

pub fn main() void {
    const config = carregarConfig() orelse
        falha_fatal("Não foi possível carregar configuração");

    std.debug.print("Config: {s}\n", .{config});
}

fn carregarConfig() ?[]const u8 {
    return "config.json";
}

noreturn em Switch

const std = @import("std");

const Comando = enum { iniciar, parar, sair };

fn executar(cmd: Comando) void {
    const mensagem: []const u8 = switch (cmd) {
        .iniciar => "Iniciando...",
        .parar => "Parando...",
        .sair => {
            std.debug.print("Encerrando programa.\n", .{});
            std.process.exit(0);
            // noreturn: compilador sabe que este branch não produz []const u8
        },
    };
    std.debug.print("{s}\n", .{mensagem});
}

pub fn main() void {
    executar(.iniciar);
}

Loop Infinito como noreturn

const std = @import("std");

fn servidor() noreturn {
    std.debug.print("Servidor iniciado.\n", .{});
    while (true) {
        // Processar requisições indefinidamente
        // Loop infinito sem break tem tipo noreturn
    }
}

Coerção de noreturn

noreturn pode ser coagido para qualquer tipo, o que é essencial para expressões condicionais:

// unreachable tem tipo noreturn, coagido para u32
const valor: u32 = if (condicao) calcular() else unreachable;

// @panic tem tipo noreturn, coagido para []const u8
const nome: []const u8 = buscar(id) orelse @panic("ID inválido");

Armadilhas Comuns

  • Confundir com void: void retorna sem valor; noreturn nunca retorna. Uma função void termina normalmente.
  • Loops com break: Um while (true) com break pode retornar — o tipo é determinado pelo break, não noreturn.
  • unreachable em safe mode: unreachable causa panic em modo safe. Em release mode, é undefined behavior. Use apenas quando tiver certeza de que o código é inalcançável.
  • Código morto: O compilador pode alertar sobre código após uma expressão noreturn, pois ele nunca será executado.

Termos Relacionados

  • void — Tipo para funções que retornam sem valor
  • Unreachable — Expressão noreturn para código inalcançável
  • Error Union — Tratamento de erros que pode levar a panic
  • Comptime — noreturn em contexto comptime

noreturn em Sistemas Embarcados e Kernels

Em programação de sistemas, noreturn é especialmente valioso. Funções de ponto de entrada de kernels, handlers de interrupção que nunca retornam, e loops de eventos em sistemas embarcados são casos naturais para noreturn:

// Ponto de entrada de um sistema embarcado
pub fn main() noreturn {
    inicializarHardware();
    inicializarPerifеricos();

    // Loop principal do sistema — nunca termina
    while (true) {
        processarEventos();
        sleep();
    }
}

// Handler de erro crítico em sistema embarcado
fn erroCritico(codigo: u8) noreturn {
    // Piscar LED de erro e travar o sistema
    while (true) {
        piscarLed(codigo);
    }
}

Comparação com Outras Linguagens

Em C, a função exit() é declarada como _Noreturn void exit(int) usando o atributo _Noreturn (C11) ou __attribute__((noreturn)) no GCC. Em Rust, o tipo ! (never type) é equivalente — funções que divergem têm tipo de retorno !. Em Zig, noreturn é um tipo de primeira classe, o que significa que pode ser usado em qualquer posição onde um tipo é esperado, incluindo em coerções implícitas dentro de expressões condicionais.

A vantagem do Zig é que noreturn integra-se naturalmente ao sistema de tipos sem sintaxe especial: um switch cujo branch retorna noreturn não precisa produzir um valor para aquele caso, e o compilador entende isso automaticamente.

Boas Práticas

  • Use para funções de erro fatal: Funções como fatal(msg) que terminam o programa são candidatas naturais a noreturn.
  • Prefira @panic a unreachable para erros reais: Se uma condição não deveria acontecer mas pode acontecer em caso de bug, use @panic — ele gera uma mensagem de erro. unreachable em Release mode causa comportamento indefinido.
  • Documente loops infinitos: Se uma função tem noreturn porque contém um loop infinito, documente isso claramente — é uma decisão de design importante.
  • Não force noreturn desnecessariamente: O compilador infere noreturn automaticamente de loops infinitos sem break e chamadas a funções noreturn.

Tutoriais Relacionados

Continue aprendendo Zig

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