Como Converter entre Tipos de String em Zig

Como Converter entre Tipos de String em Zig

Em Zig, não existe um único tipo “string”. Em vez disso, existem vários tipos relacionados que representam sequências de bytes. Entender como converter entre eles é essencial para trabalhar eficientemente com texto em Zig.

Os Tipos de String em Zig

Os principais tipos usados como strings em Zig são:

TipoDescrição
[]const u8Slice imutável de bytes (o tipo mais comum para strings)
[]u8Slice mutável de bytes
[N]u8Array de tamanho fixo
[:0]const u8Slice com terminador nulo (sentinel)
[*:0]const u8Ponteiro para string com terminador nulo (estilo C)
[*]const u8Ponteiro para sequência de bytes (sem tamanho)

Literais de String

Literais de string em Zig são do tipo *const [N:0]u8, que coerce automaticamente para []const u8 e [:0]const u8.

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // Literal é *const [9:0]u8, mas coerce para []const u8
    const texto: []const u8 = "Olá Mundo";
    try stdout.print("Tipo slice: \"{s}\" (len={d})\n", .{ texto, texto.len });

    // Também coerce para [:0]const u8 (com sentinel)
    const sentinel: [:0]const u8 = "Olá Mundo";
    try stdout.print("Com sentinel: \"{s}\" (len={d})\n", .{ sentinel, sentinel.len });

    // Acessar o terminador nulo
    try stdout.print("Byte após fim: {d}\n", .{sentinel[sentinel.len]});
}

Saída esperada:

Tipo slice: "Olá Mundo" (len=9)
Com sentinel: "Olá Mundo" (len=9)
Byte após fim: 0

Converter Array para Slice

Arrays de u8 podem ser convertidos para slices usando o operador de endereço &.

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // Array fixo para slice
    const array: [5]u8 = .{ 'H', 'e', 'l', 'l', 'o' };
    const slice: []const u8 = &array;
    try stdout.print("Array como slice: \"{s}\"\n", .{slice});

    // Array mutável para slice mutável
    var buf: [10]u8 = undefined;
    @memcpy(buf[0..5], "Mundo");
    const slice_mut: []u8 = buf[0..5];
    try stdout.print("Slice mutável: \"{s}\"\n", .{slice_mut});

    // Modificar através do slice mutável
    slice_mut[0] = 'Z';
    try stdout.print("Após modificar: \"{s}\"\n", .{slice_mut});
}

Saída esperada:

Array como slice: "Hello"
Slice mutável: "Mundo"
Após modificar: "Zundo"

Converter Slice para Array de Tamanho Fixo

Use slice[0..N].* para converter um slice em um array de tamanho fixo (quando o tamanho é conhecido em comptime).

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    const slice: []const u8 = "Hello Zig!";

    // Converter slice para array de tamanho fixo
    const array: [5]u8 = slice[0..5].*;
    try stdout.print("Array fixo: \"{s}\"\n", .{&array});

    // Usar com funções que esperam arrays
    const primeiros: [3]u8 = slice[0..3].*;
    try stdout.print("Primeiros 3: \"{s}\"\n", .{&primeiros});
}

Saída esperada:

Array fixo: "Hello"
Primeiros 3: "Hel"

Trabalhar com Strings Sentinel (Terminador Nulo)

Strings com terminador nulo são necessárias para interoperar com código C.

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // [:0]const u8 para []const u8 - apenas perde o sentinel
    const sentinel: [:0]const u8 = "Hello";
    const slice: []const u8 = sentinel;
    try stdout.print("Sentinel -> Slice: \"{s}\" (len={d})\n", .{ slice, slice.len });

    // []const u8 para [:0]const u8 - precisa verificar ou criar
    // Se souber que tem sentinel, use @ptrCast
    // Para criar uma cópia com sentinel:
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const sem_sentinel: []const u8 = "Texto dinâmico";
    const com_sentinel = try allocator.dupeZ(u8, sem_sentinel);
    defer allocator.free(com_sentinel);
    try stdout.print("Com sentinel: \"{s}\" (byte final: {d})\n", .{
        com_sentinel, com_sentinel[com_sentinel.len],
    });
}

Saída esperada:

Sentinel -> Slice: "Hello" (len=5)
Com sentinel: "Texto dinâmico" (byte final: 0)

Converter Ponteiro Estilo C para Slice

Ao trabalhar com APIs C, você frequentemente recebe [*:0]const u8. Use std.mem.span para converter.

const std = @import("std");

// Simulando uma função C que retorna string
fn funcaoC() [*:0]const u8 {
    return "Retorno da função C";
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    const c_str = funcaoC();

    // Converter ponteiro C para slice Zig usando std.mem.span
    const zig_str: [:0]const u8 = std.mem.span(c_str);
    try stdout.print("String C como Zig: \"{s}\" (len={d})\n", .{ zig_str, zig_str.len });

    // Também pode usar como []const u8
    const slice: []const u8 = std.mem.span(c_str);
    try stdout.print("Como slice: \"{s}\"\n", .{slice});
}

Saída esperada:

String C como Zig: "Retorno da função C" (len=19)
Como slice: "Retorno da função C"

Criar String a Partir de Bytes Individuais

const std = @import("std");

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

    // Criar string a partir de bytes individuais
    var builder = std.ArrayList(u8).init(allocator);
    defer builder.deinit();

    const bytes = [_]u8{ 72, 101, 108, 108, 111 }; // "Hello" em ASCII
    try builder.appendSlice(&bytes);
    try builder.append('!');

    try stdout.print("De bytes: \"{s}\"\n", .{builder.items});

    // Converter ArrayList para slice owned
    const resultado = try builder.toOwnedSlice();
    defer allocator.free(resultado);
    try stdout.print("Owned: \"{s}\" (len={d})\n", .{ resultado, resultado.len });
}

Saída esperada:

De bytes: "Hello!"
Owned: "Hello!" (len=6)

Duplicar Strings com Alocador

Para criar cópias independentes de strings, use allocator.dupe ou allocator.dupeZ.

const std = @import("std");

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

    const original = "Texto original";

    // dupe: cópia sem sentinel
    const copia = try allocator.dupe(u8, original);
    defer allocator.free(copia);
    try stdout.print("Cópia: \"{s}\"\n", .{copia});

    // dupeZ: cópia com sentinel nulo
    const copia_z = try allocator.dupeZ(u8, original);
    defer allocator.free(copia_z);
    try stdout.print("Cópia Z: \"{s}\" (sentinel: {d})\n", .{
        copia_z, copia_z[copia_z.len],
    });
}

Saída esperada:

Cópia: "Texto original"
Cópia Z: "Texto original" (sentinel: 0)

Tabela de Conversões

DeParaMétodo
[N]u8[]const u8&array
[]const u8[N]u8slice[0..N].*
[:0]const u8[]const u8Conversão implícita
[]const u8[:0]const u8allocator.dupeZ()
[*:0]const u8[:0]const u8std.mem.span()
[]const u8Cópia independenteallocator.dupe()

Veja Também

Continue aprendendo Zig

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