Sentinel Mismatch — Como Resolver em Zig

Sentinel Mismatch — Como Resolver em Zig

O Que Este Erro Significa

O erro de sentinel mismatch ocorre quando há incompatibilidade no valor sentinel (terminador) entre tipos de slice ou array. Em Zig, um sentinel é um valor especial colocado imediatamente após o final de um slice ou array para marcar seu término. O sentinel mais comum é 0 (null terminator), usado para compatibilidade com strings C.

Mensagens do compilador:

error: expected type '[:0]const u8', found '[]const u8'
error: expected sentinel value '0', found 'null'

Sentinels são parte do tipo em Zig — [:0]const u8 (slice com sentinel zero) é um tipo diferente de []const u8 (slice sem sentinel).

Causas Comuns

1. Passar Slice sem Sentinel onde Sentinel É Esperado

const std = @import("std");

fn funcaoC(texto: [*:0]const u8) void {
    _ = texto;
}

pub fn main() void {
    const dados: []const u8 = "olá";
    funcaoC(dados.ptr); // ERRO: []const u8 não tem sentinel
}

2. Resultado de Operação de Slice Perde Sentinel

pub fn main() void {
    const texto: [:0]const u8 = "olá mundo";
    // Slicing perde o sentinel
    const parte = texto[0..3]; // tipo: *const [3]u8, sem sentinel
    const slice_sem_sentinel: []const u8 = parte;
    // Não pode usar onde [:0]const u8 é esperado
    _ = slice_sem_sentinel;
}

3. Alocação sem Sentinel

const std = @import("std");

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

    const buffer = try allocator.alloc(u8, 100);
    defer allocator.free(buffer);

    // buffer é []u8, não [:0]u8
    // Não pode ser usado onde [:0]const u8 é esperado
    _ = buffer;
}

4. Concatenação que Perde Sentinel

const std = @import("std");

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

    const a: []const u8 = "olá";
    const b: []const u8 = " mundo";
    const resultado = try std.mem.concat(allocator, u8, &.{ a, b });
    defer allocator.free(resultado);
    // resultado é []u8, não [:0]u8
    _ = resultado;
}

5. Função que Espera String C

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

pub fn main() void {
    var buffer: [100]u8 = undefined;
    const slice: []u8 = &buffer;
    // ERRO: puts espera [*:0]const u8 (string C)
    _ = c.puts(slice.ptr);
}

Como Corrigir

Solucao 1: Usar String Literal Diretamente

String literals em Zig já são sentinel-terminated:

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

pub fn main() void {
    // String literal é *const [N:0]u8, que coerce para [*:0]const u8
    _ = c.puts("olá mundo");
}

Solucao 2: Alocar com Sentinel

const std = @import("std");

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

    // allocSentinel adiciona o terminador automaticamente
    const buffer = try allocator.allocSentinel(u8, 100, 0);
    defer allocator.free(buffer);
    // buffer é [:0]u8 — compatível com funções C
    _ = buffer;
}

Solucao 3: Usar std.fmt.bufPrintZ para Strings C

const std = @import("std");

pub fn main() void {
    var buf: [100]u8 = undefined;
    // bufPrintZ retorna [:0]u8 (com null terminator)
    const resultado = std.fmt.bufPrintZ(&buf, "valor: {d}", .{@as(u32, 42)}) catch unreachable;
    std.debug.print("Resultado (com \\0): {s}\n", .{resultado});
}

Solucao 4: Converter Slice para Sentinel Slice

const std = @import("std");

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

    const texto: []const u8 = "olá";
    // dupeZ cria cópia com sentinel zero
    const texto_z = try allocator.dupeZ(u8, texto);
    defer allocator.free(texto_z);
    // texto_z é [:0]u8
    _ = texto_z;
}

Solucao 5: Usar std.mem.span para Converter de C

const std = @import("std");

fn funcaoQueRetornaStringC() [*:0]const u8 {
    return "dados da API C";
}

pub fn main() void {
    const c_str = funcaoQueRetornaStringC();
    // span converte [*:0]const u8 para [:0]const u8
    const zig_str: [:0]const u8 = std.mem.span(c_str);
    // E pode usar como []const u8 também
    const slice: []const u8 = zig_str;
    std.debug.print("{s} (len: {})\n", .{ slice, slice.len });
}

Solucao 6: Slicing com Sentinel Preservado

pub fn main() void {
    const texto: [:0]const u8 = "olá mundo";

    // Slicing com sentinel preserva o sentinel se for até o final
    const parte = texto[0..texto.len :0]; // [:0]const u8
    _ = parte;

    // Slicing parcial perde o sentinel
    const parcial = texto[0..3]; // *const [3]u8 — sem sentinel
    _ = parcial;
}

Tipos com Sentinel

TipoDescrição
[:0]const u8Slice de bytes com null terminator
[:0]u8Slice mutável com null terminator
[*:0]const u8Ponteiro para muitos bytes, terminado em 0 (string C)
*const [N:0]u8Ponteiro para array de N bytes com sentinel
[N:0]u8Array de N bytes com sentinel

Interoperabilidade com C

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

pub fn main() void {
    // De Zig para C
    const zig_str: [:0]const u8 = "olá";
    const c_len = c.strlen(zig_str); // Funciona — sentinel garante \0
    _ = c_len;

    // De C para Zig
    const c_str: [*:0]const u8 = "mundo";
    const zig_slice = std.mem.span(c_str); // [:0]const u8
    std.debug.print("{s}\n", .{zig_slice});
}

Erros Relacionados

Continue aprendendo Zig

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