Como Concatenar Strings em Zig

Como Concatenar Strings em Zig

Em Zig, strings são slices de bytes ([]const u8), e a linguagem não possui um operador + para concatenação como em outras linguagens. Em vez disso, Zig oferece abordagens explícitas e eficientes que lhe dão controle total sobre a alocação de memória.

Nesta receita, vamos explorar várias técnicas para concatenar strings em Zig.

Concatenação em Tempo de Compilação (comptime)

A forma mais simples de concatenar strings é em tempo de compilação, usando o operador ++. Isso funciona apenas quando ambos os operandos são conhecidos em comptime.

const std = @import("std");

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

    // Concatenação em comptime com ++
    const saudacao = "Olá, " ++ "Mundo!";
    try stdout.print("{s}\n", .{saudacao});

    // Concatenar múltiplas partes
    const frase = "Zig " ++ "é " ++ "uma " ++ "linguagem " ++ "incrível!";
    try stdout.print("{s}\n", .{frase});

    // Concatenar com caracteres especiais
    const com_newline = "Primeira linha\n" ++ "Segunda linha\n";
    try stdout.print("{s}", .{com_newline});

    // Repetição de strings em comptime
    const separador = "-" ** 20;
    try stdout.print("{s}\n", .{separador});
}

Saída esperada:

Olá, Mundo!
Zig é uma linguagem incrível!
Primeira linha
Segunda linha
--------------------

Concatenação com std.mem.concat

A função std.mem.concat aloca memória e concatena múltiplos slices em um único slice.

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();

    // Concatenar duas strings
    const nome = "Maria";
    const sobrenome = "Silva";
    const nome_completo = try std.mem.concat(allocator, u8, &.{ nome, " ", sobrenome });
    defer allocator.free(nome_completo);
    try stdout.print("Nome: {s}\n", .{nome_completo});

    // Concatenar múltiplas partes
    const caminho = try std.mem.concat(allocator, u8, &.{ "/home", "/", "usuario", "/", "documentos" });
    defer allocator.free(caminho);
    try stdout.print("Caminho: {s}\n", .{caminho});
}

Saída esperada:

Nome: Maria Silva
Caminho: /home/usuario/documentos

Concatenação com std.fmt.allocPrint

Usar std.fmt.allocPrint é uma abordagem muito flexível que permite incluir formatação durante a concatenação.

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();

    // Concatenar com formatação
    const nome = "João";
    const idade: u32 = 28;
    const apresentacao = try std.fmt.allocPrint(allocator, "Meu nome é {s} e tenho {d} anos.", .{ nome, idade });
    defer allocator.free(apresentacao);
    try stdout.print("{s}\n", .{apresentacao});

    // Construir URL com parâmetros
    const base = "https://api.exemplo.com";
    const endpoint = "usuarios";
    const id: u64 = 42;
    const url = try std.fmt.allocPrint(allocator, "{s}/{s}/{d}", .{ base, endpoint, id });
    defer allocator.free(url);
    try stdout.print("URL: {s}\n", .{url});

    // Concatenar string com número formatado
    const preco: f64 = 49.99;
    const texto = try std.fmt.allocPrint(allocator, "Preço: R$ {d:.2}", .{preco});
    defer allocator.free(texto);
    try stdout.print("{s}\n", .{texto});
}

Saída esperada:

Meu nome é João e tenho 28 anos.
URL: https://api.exemplo.com/usuarios/42
Preço: R$ 49.99

Concatenação com Buffer Fixo (Sem Alocação)

Quando você quer evitar alocações dinâmicas, pode usar std.fmt.bufPrint com um buffer de tamanho fixo.

const std = @import("std");

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

    var buffer: [256]u8 = undefined;

    // Concatenar em buffer fixo
    const parte1 = "Olá";
    const parte2 = "Mundo";
    const resultado = try std.fmt.bufPrint(&buffer, "{s}, {s}!", .{ parte1, parte2 });
    try stdout.print("Resultado: {s}\n", .{resultado});

    // Múltiplas partes com formatação
    const dia: u32 = 21;
    const mes = "fevereiro";
    const ano: u32 = 2026;
    const data = try std.fmt.bufPrint(&buffer, "{d} de {s} de {d}", .{ dia, mes, ano });
    try stdout.print("Data: {s}\n", .{data});
}

Saída esperada:

Resultado: Olá, Mundo!
Data: 21 de fevereiro de 2026

Concatenação com ArrayList (Construção Incremental)

Para construir strings de forma incremental (como um StringBuilder em Java), use std.ArrayList(u8).

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();

    // Usar ArrayList como StringBuilder
    var builder = std.ArrayList(u8).init(allocator);
    defer builder.deinit();

    // Adicionar texto incrementalmente
    try builder.appendSlice("SELECT * FROM usuarios");
    try builder.appendSlice(" WHERE ativo = true");
    try builder.appendSlice(" ORDER BY nome");
    try builder.appendSlice(" LIMIT 10;");

    const query = builder.items;
    try stdout.print("Query: {s}\n", .{query});

    // Construir com writer do ArrayList
    var builder2 = std.ArrayList(u8).init(allocator);
    defer builder2.deinit();

    const writer = builder2.writer();
    try writer.print("Item {d}: {s} - R$ {d:.2}", .{ 1, "Teclado", 199.90 });

    try stdout.print("Produto: {s}\n", .{builder2.items});
}

Saída esperada:

Query: SELECT * FROM usuarios WHERE ativo = true ORDER BY nome LIMIT 10;
Produto: Item 1: Teclado - R$ 199.90

Juntar (Join) Array de Strings

Para juntar múltiplas strings com um separador, use std.mem.join.

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();

    // Juntar palavras com espaço
    const palavras = [_][]const u8{ "Zig", "é", "fantástico" };
    const frase = try std.mem.join(allocator, " ", &palavras);
    defer allocator.free(frase);
    try stdout.print("Frase: {s}\n", .{frase});

    // Juntar com vírgula
    const frutas = [_][]const u8{ "maçã", "banana", "laranja", "uva" };
    const lista = try std.mem.join(allocator, ", ", &frutas);
    defer allocator.free(lista);
    try stdout.print("Frutas: {s}\n", .{lista});

    // Juntar caminhos com separador
    const partes = [_][]const u8{ "home", "usuario", "projetos", "meu-app" };
    const caminho = try std.mem.join(allocator, "/", &partes);
    defer allocator.free(caminho);
    try stdout.print("Caminho: /{s}\n", .{caminho});
}

Saída esperada:

Frase: Zig é fantástico
Frutas: maçã, banana, laranja, uva
Caminho: /home/usuario/projetos/meu-app

Tabela Comparativa de Métodos

MétodoAlocaçãoUso Ideal
++ (comptime)NenhumaStrings conhecidas em compilação
std.mem.concatHeapConcatenar slices existentes
std.fmt.allocPrintHeapFormatação + concatenação
std.fmt.bufPrintStackEvitar alocação heap
ArrayList(u8)HeapConstrução incremental
std.mem.joinHeapJuntar com separador

Veja Também

Continue aprendendo Zig

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