Sentinel em Zig — O que é e Como Usar

Sentinel em Zig — O que é e Como Usar

Definição

Um sentinel-terminated array (array terminado por sentinela) em Zig é um array ou slice que possui um valor especial — o sentinel — imediatamente após o último elemento. O exemplo mais comum é a string terminada em zero ([:0]u8), equivalente às strings C (char* com \0 no final).

A sintaxe [N:s]T declara um array de N elementos do tipo T com sentinela s. Para slices, [:s]T indica um slice com sentinel.

Por que Sentinels Importam

  1. Interoperabilidade com C: APIs C esperam strings terminadas em null. Zig facilita a criação e uso desses tipos.
  2. Segurança de tipos: O tipo carrega a informação do sentinel, garantindo em tempo de compilação que o terminador está presente.
  3. Compatibilidade: Sentinel-terminated slices podem ser convertidos para slices normais sem custo.
  4. String literals: Todas as string literals em Zig são *const [N:0]u8 — terminadas em zero automaticamente.

Exemplo Prático

Strings Terminadas em Zero

const std = @import("std");

pub fn main() void {
    // String literal é automaticamente sentinel-terminated
    const mensagem: [:0]const u8 = "Olá, Zig!";

    // Acessar o sentinel
    std.debug.print("Último char: '{}'\n", .{mensagem[mensagem.len]}); // 0

    // Passar para função C
    _ = std.c.printf("%s\n", mensagem.ptr);
}

Arrays com Sentinel Customizado

// Array de 3 elementos terminado com 0xFF
const dados: [3:0xFF]u8 = .{ 10, 20, 30 };

// O elemento na posição [3] é 0xFF (o sentinel)
std.debug.print("Sentinel: {}\n", .{dados[3]}); // 255

Convertendo entre Tipos

const std = @import("std");

pub fn main() void {
    // Sentinel-terminated → slice normal (sem custo)
    const sentinel_slice: [:0]const u8 = "Zig Brasil";
    const slice_normal: []const u8 = sentinel_slice;
    _ = slice_normal;

    // Array → sentinel slice
    var array: [5:0]u8 = .{ 'H', 'e', 'l', 'l', 'o' };
    const s: [:0]u8 = &array;
    _ = s;
}

Interop com C

const std = @import("std");
const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("string.h");
});

pub fn main() void {
    const zig_string: [:0]const u8 = "Texto do Zig";

    // strlen espera [*:0]const u8 — compatível automaticamente
    const tamanho = c.strlen(zig_string);
    std.debug.print("Tamanho via C: {}\n", .{tamanho});
}

Criando Sentinel Slice Dinamicamente

fn criarStringC(allocator: std.mem.Allocator, texto: []const u8) ![:0]u8 {
    const resultado = try allocator.allocSentinel(u8, texto.len, 0);
    @memcpy(resultado, texto);
    return resultado;
}

Tipos com Sentinel

TipoDescrição
[N:0]u8Array de N bytes + null terminator
[:0]u8Slice de bytes com null terminator
[*:0]u8Many-pointer terminado em null
[N:s]TArray de N elementos com sentinel s

Armadilhas Comuns

  • Assumir que todo []const u8 é terminado em zero: Slices normais NÃO têm sentinel. Só [:0]const u8 garante o terminador.
  • Acessar além do sentinel: O sentinel está em slice[slice.len], mas acessar slice[slice.len + 1] é comportamento indefinido.
  • Esquecer de usar allocSentinel: Ao alocar memória para strings que serão passadas para C, use allocSentinel em vez de alloc.
  • Converter sem cuidado: Converter []u8 para [:0]u8 requer que o sentinela realmente exista na memória. Use std.mem.sliceTo para conversão segura.

Termos Relacionados

  • Slice — Referência a sequência de memória
  • Pointer Types — Tipos de ponteiro em Zig
  • usize — Tipo de índice e comprimento

Tutoriais Relacionados

Continue aprendendo Zig

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