std.BoundedArray — Array de Capacidade Fixa
O std.BoundedArray é uma estrutura de dados que oferece uma interface similar ao ArrayList, mas com capacidade máxima definida em tempo de compilação. Ele não requer nenhum alocador de memória, pois armazena todos os dados diretamente na stack (ou inline na struct que o contém). É ideal para situações onde o tamanho máximo é conhecido e alocação dinâmica não é desejável — como em sistemas embarcados, contextos de tempo real ou hot paths de desempenho crítico.
Visão Geral
const std = @import("std");
const BoundedArray = std.BoundedArray;
Tipo e Assinatura
pub fn BoundedArray(comptime T: type, comptime capacity: usize) type
O tipo resultante contém:
- Um array fixo
[capacity]Tpara armazenamento - Um campo
lenindicando quantos elementos estão em uso
Funções Principais
Criação
// Cria array vazio
pub fn init(len: usize) error{Overflow}!BoundedArray(T, N)
// Cria a partir de um slice
pub fn fromSlice(slice: []const T) error{Overflow}!BoundedArray(T, N)
Adição e Remoção
// Adiciona ao final
pub fn append(self: *Self, item: T) error{Overflow}!void
// Adiciona um slice inteiro
pub fn appendSlice(self: *Self, items: []const T) error{Overflow}!void
// Remove e retorna o último
pub fn pop(self: *Self) T
// Remove e retorna o último, ou null
pub fn popOrNull(self: *Self) ?T
// Remove em índice específico
pub fn orderedRemove(self: *Self, index: usize) T
// Remove por troca (O(1))
pub fn swapRemove(self: *Self, index: usize) T
Acesso
// Retorna slice dos elementos ativos
pub fn slice(self: *Self) []T
pub fn constSlice(self: *const Self) []const T
// Acesso por índice com bounds checking
pub fn get(self: Self, index: usize) T
// Define valor em índice
pub fn set(self: *Self, index: usize, value: T) void
// Número de elementos
// Campo: self.len
Redimensionamento
// Redefine o comprimento
pub fn resize(self: *Self, new_len: usize) error{Overflow}!void
// Limpa todos os elementos
pub fn clear(self: *Self) void
Exemplo 1: Buffer de Mensagens sem Alocação
const std = @import("std");
const MAX_MENSAGENS = 10;
const Mensagem = struct {
texto: [64]u8,
texto_len: usize,
prioridade: u8,
};
pub fn main() !void {
var fila = try std.BoundedArray(Mensagem, MAX_MENSAGENS).init(0);
// Adiciona mensagens
const textos = [_][]const u8{
"Sistema iniciado",
"Conexão estabelecida",
"Dados recebidos",
};
for (textos, 0..) |texto, i| {
var msg: Mensagem = .{
.texto = [_]u8{0} ** 64,
.texto_len = texto.len,
.prioridade = @intCast(i),
};
@memcpy(msg.texto[0..texto.len], texto);
try fila.append(msg);
}
const stdout = std.io.getStdOut().writer();
try stdout.print("Mensagens na fila: {d}/{d}\n", .{ fila.len, MAX_MENSAGENS });
for (fila.constSlice()) |msg| {
try stdout.print(" [{d}] {s}\n", .{
msg.prioridade,
msg.texto[0..msg.texto_len],
});
}
}
Exemplo 2: Coletor de Argumentos de Linha de Comando
const std = @import("std");
const MAX_ARGS = 32;
const ArgsParser = struct {
flags: std.BoundedArray([]const u8, MAX_ARGS),
valores: std.BoundedArray([]const u8, MAX_ARGS),
pub fn init() ArgsParser {
return .{
.flags = std.BoundedArray([]const u8, MAX_ARGS).init(0) catch unreachable,
.valores = std.BoundedArray([]const u8, MAX_ARGS).init(0) catch unreachable,
};
}
pub fn parse(self: *ArgsParser, args: []const []const u8) !void {
for (args) |arg| {
if (std.mem.startsWith(u8, arg, "--")) {
try self.flags.append(arg[2..]);
} else {
try self.valores.append(arg);
}
}
}
pub fn temFlag(self: *const ArgsParser, nome: []const u8) bool {
for (self.flags.constSlice()) |flag| {
if (std.mem.eql(u8, flag, nome)) return true;
}
return false;
}
};
pub fn main() !void {
var parser = ArgsParser.init();
const args_simulados = [_][]const u8{
"arquivo.txt", "--verbose", "--output", "resultado.txt",
};
try parser.parse(&args_simulados);
const stdout = std.io.getStdOut().writer();
try stdout.print("Flags: {d}\n", .{parser.flags.len});
try stdout.print("Valores: {d}\n", .{parser.valores.len});
try stdout.print("Verbose: {}\n", .{parser.temFlag("verbose")});
}
Exemplo 3: Buffer Circular com BoundedArray
const std = @import("std");
fn BufferCircular(comptime T: type, comptime N: usize) type {
return struct {
dados: std.BoundedArray(T, N),
inicio: usize = 0,
const Self = @This();
pub fn init() Self {
return .{
.dados = std.BoundedArray(T, N).init(0) catch unreachable,
};
}
pub fn push(self: *Self, valor: T) void {
if (self.dados.len < N) {
self.dados.append(valor) catch unreachable;
} else {
self.dados.set(self.inicio, valor);
self.inicio = (self.inicio + 1) % N;
}
}
pub fn ultimosN(self: *const Self, n: usize) void {
const total = @min(n, self.dados.len);
const stdout = std.io.getStdOut().writer() catch return;
_ = stdout;
var i: usize = 0;
while (i < total) : (i += 1) {
const idx = (self.inicio + self.dados.len - total + i) % self.dados.len;
std.debug.print("{d} ", .{self.dados.constSlice()[idx]});
}
std.debug.print("\n", .{});
}
};
}
pub fn main() void {
var buf = BufferCircular(i32, 5).init();
// Insere mais que a capacidade
for (0..8) |i| {
buf.push(@intCast(i * 10));
}
std.debug.print("Buffer (últimos 5): ", .{});
const slice = buf.dados.constSlice();
for (slice) |v| {
std.debug.print("{d} ", .{v});
}
std.debug.print("\n", .{});
std.debug.print("Tamanho: {d}\n", .{buf.dados.len});
}
BoundedArray vs ArrayList
| Característica | BoundedArray | ArrayList |
|---|---|---|
| Alocação | Stack / inline | Heap |
| Requer allocator | Não | Sim |
| Capacidade | Fixa (comptime) | Dinâmica |
| Overhead | Zero | Ponteiro + metadados |
| Uso em embarcados | Ideal | Depende do allocator |
Módulos Relacionados
- std.ArrayList — Array dinâmico com alocação
- std.MultiArrayList — SoA para structs
- std.mem — Operações de memória