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
| Tipo | Descrição |
|---|---|
[:0]const u8 | Slice de bytes com null terminator |
[:0]u8 | Slice mutável com null terminator |
[*:0]const u8 | Ponteiro para muitos bytes, terminado em 0 (string C) |
*const [N:0]u8 | Ponteiro para array de N bytes com sentinel |
[N:0]u8 | Array 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
- Slice type mismatch — Incompatibilidade de tipos de slice
- Type coercion failed — Falha na coerção de tipos
- @cImport failed — Falha ao importar cabeçalho C
- Buffer overrun — Acesso além dos limites do buffer