void em Zig — O que é e Como Usar

void em Zig — O que é e Como Usar

Definição

void é o tipo em Zig que representa ausência de valor. Ele ocupa zero bytes de memória e é usado principalmente como tipo de retorno de funções que não produzem resultado. Diferente de C, onde void é especial e não pode ser usado como valor, em Zig void é um tipo de primeira classe — pode ser atribuído a variáveis, usado em genéricos e armazenado em structs.

O valor literal de void é {}.

Por que void Importa

  1. Retorno de funções: Indica que uma função executa efeito colateral sem produzir valor.
  2. Genéricos: Permite criar contêineres onde o “valor” é ignorado (ex: HashSet usando HashMap com valor void).
  3. Otimização de memória: Campos void em structs não ocupam espaço.
  4. Consistência do sistema de tipos: Todo tipo em Zig pode ser usado uniformemente, incluindo void.

Exemplo Prático

Função com Retorno void

const std = @import("std");

fn saudar(nome: []const u8) void {
    std.debug.print("Olá, {s}!\n", .{nome});
    // Sem return explícito — retorna void implicitamente
}

fn processar(dados: []u8) void {
    for (dados) |*byte| {
        byte.* = byte.* +% 1; // incrementa cada byte
    }
    return; // return explícito de void (opcional)
}

pub fn main() void {
    saudar("Zig Brasil");

    var buffer = [_]u8{ 1, 2, 3 };
    processar(&buffer);
    std.debug.print("{any}\n", .{buffer}); // { 2, 3, 4 }
}

HashSet com void como Valor

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // HashMap com valor void funciona como um Set
    var conjunto = std.AutoHashMap([]const u8, void).init(allocator);
    defer conjunto.deinit();

    try conjunto.put("zig", {});
    try conjunto.put("rust", {});
    try conjunto.put("go", {});

    // Verificar pertencimento
    const tem_zig = conjunto.contains("zig");
    std.debug.print("Tem zig: {}\n", .{tem_zig}); // true
}

void em Tipos Genéricos

const std = @import("std");

fn Wrapper(comptime T: type) type {
    return struct {
        valor: T,

        pub fn init(v: T) @This() {
            return .{ .valor = v };
        }

        pub fn temValor() bool {
            return T != void;
        }
    };
}

pub fn main() void {
    const com_valor = Wrapper(u32).init(42);
    const sem_valor = Wrapper(void).init({});

    std.debug.print("Com valor: {}\n", .{Wrapper(u32).temValor()});  // true
    std.debug.print("Sem valor: {}\n", .{Wrapper(void).temValor()}); // false
    _ = com_valor;
    _ = sem_valor;
}

void vs noreturn

TipoSignificadoExemplo
voidRetorna, mas sem valor útilfn processar() void
noreturnNunca retornafn sair() noreturn

Quando Usar void

  • Funções de efeito colateral puro: void é o retorno correto para funções que escrevem em arquivo, modificam estado global ou simplesmente imprimem saída.
  • !void para falhas sem resultado: O padrão mais comum em Zig para funções que podem falhar é retornar !void — o chamador usa try para propagar o erro.
  • HashSet via HashMap: Usar void como valor de um HashMap transforma-o em um conjunto (set) sem custo extra de memória.
  • Campos de struct em genéricos: Um campo valor: void em um struct genérico ocupa zero bytes, permitindo structs que opcionalmente carregam dados.

!void na Prática

O tipo !void é tão comum em Zig que merece atenção especial. A função main em quase todos os programas Zig reais retorna !void:

pub fn main() !void {
    const arquivo = try std.fs.cwd().openFile("dados.txt", .{});
    defer arquivo.close();

    var buf: [1024]u8 = undefined;
    const lidos = try arquivo.readAll(&buf);
    std.debug.print("Lidos: {} bytes\n", .{lidos});
    // Retorna void implicitamente ao fim — ou propaga erro com try
}

A ausência de return no final de uma função !void (ou void) é válida — o compilador insere o return {} automaticamente.

Armadilhas Comuns

  • void vs noreturn: void retorna normalmente (sem valor); noreturn indica que a função nunca retorna (ex: @panic, loop infinito, std.process.exit).
  • Literal void: O valor de void é {}, não null. null é para optionals.
  • Comparação: Dois valores void são sempre iguais ({} == {} é true).
  • Em error unions: !void significa “pode retornar erro ou void”. É o tipo de retorno mais comum para funções que podem falhar sem produzir resultado.

Termos Relacionados

  • noreturn — Tipo para funções que nunca retornam
  • Optional — Tipo que pode ser nulo (diferente de void)
  • Error Union!void para funções que podem falhar
  • Struct — Campos void ocupam zero bytes

Tutoriais Relacionados

Continue aprendendo Zig

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