index out of bounds — Como Resolver em Zig
O Que Este Erro Significa
O erro index out of bounds é um erro de tempo de execução (runtime) que ocorre quando você tenta acessar um elemento de um array ou slice usando um índice que está fora dos limites válidos. Em Zig, arrays e slices são indexados a partir de zero, então um array de tamanho N tem índices válidos de 0 a N-1. Tentar acessar o índice N ou superior (ou um índice negativo em tipos signed) causa esse erro.
Diferente de C, onde acessos fora dos limites simplesmente resultam em comportamento indefinido silencioso, Zig detecta esse erro em builds de Debug e Safe, gerando um panic com uma mensagem clara.
Causas Comuns
1. Acesso com Índice Igual ao Tamanho
const std = @import("std");
pub fn main() void {
const arr = [_]u32{ 10, 20, 30 };
const ultimo = arr[3]; // PANIC: index 3 out of bounds para array de tamanho 3
// Índices válidos: 0, 1, 2
_ = ultimo;
}
2. Loop com Condição Errada
const std = @import("std");
pub fn main() void {
const dados = [_]u8{ 'a', 'b', 'c' };
var i: usize = 0;
while (i <= dados.len) : (i += 1) { // BUG: deveria ser < não <=
std.debug.print("{c}\n", .{dados[i]}); // PANIC quando i == 3
}
}
3. Slice Vazio Acessado
pub fn main() void {
const vazio: []const u8 = &[_]u8{};
const primeiro = vazio[0]; // PANIC: index 0 out of bounds para slice de tamanho 0
_ = primeiro;
}
4. Off-by-One em Cálculo de Índice
pub fn main() void {
const nomes = [_][]const u8{ "Ana", "Bruno", "Carlos" };
const quantidade = nomes.len; // 3
const ultimo = nomes[quantidade]; // PANIC: deveria ser quantidade - 1
_ = ultimo;
}
5. Índice Dinâmico Não Validado
fn obterElemento(arr: []const u32, idx: usize) u32 {
return arr[idx]; // PANIC se idx >= arr.len
}
Como Corrigir
Solução 1: Usar < ao Invés de <=
const std = @import("std");
pub fn main() void {
const dados = [_]u8{ 'a', 'b', 'c' };
var i: usize = 0;
while (i < dados.len) : (i += 1) { // Correto: < ao invés de <=
std.debug.print("{c}\n", .{dados[i]});
}
}
Solução 2: Usar Iteração for (Preferível)
Em Zig, use for para iterar — ele nunca causa out of bounds:
const std = @import("std");
pub fn main() void {
const dados = [_]u8{ 'a', 'b', 'c' };
for (dados) |byte| {
std.debug.print("{c}\n", .{byte}); // Seguro: sem índice manual
}
}
Se precisar do índice:
for (dados, 0..) |byte, i| {
std.debug.print("[{}] = {c}\n", .{ i, byte });
}
Solução 3: Acessar o Último Elemento Corretamente
pub fn main() void {
const nomes = [_][]const u8{ "Ana", "Bruno", "Carlos" };
const ultimo = nomes[nomes.len - 1]; // Correto: "Carlos"
_ = ultimo;
}
Solução 4: Verificar Antes de Acessar
fn obterElemento(arr: []const u32, idx: usize) ?u32 {
if (idx >= arr.len) return null; // Verificação de limites
return arr[idx];
}
pub fn main() void {
const arr = [_]u32{ 10, 20, 30 };
if (obterElemento(&arr, 5)) |valor| {
_ = valor;
} else {
// Índice fora dos limites — tratar o caso
}
}
Solução 5: Verificar Slice Vazio
pub fn main() void {
const dados: []const u8 = &[_]u8{};
if (dados.len > 0) {
const primeiro = dados[0]; // Seguro
_ = primeiro;
}
}
Solução 6: Usar Slicing Seguro
pub fn main() void {
const arr = [_]u32{ 10, 20, 30, 40, 50 };
// Slicing com limites seguros
const inicio: usize = 1;
const fim: usize = @min(4, arr.len); // Garante que não excede o tamanho
const sub = arr[inicio..fim];
_ = sub;
}
Debug: Encontrando a Causa
Stack Trace
Quando esse panic ocorre, Zig imprime um stack trace que mostra exatamente onde aconteceu:
thread 1 panic: index out of bounds
/home/user/src/main.zig:12:23: 0x20c4a8 in main
const valor = arr[idx];
^
Usar std.debug.print Para Inspecionar
fn processar(dados: []const u32, idx: usize) void {
std.debug.print("dados.len={}, idx={}\n", .{ dados.len, idx });
// Agora você pode ver os valores antes do acesso
if (idx < dados.len) {
_ = dados[idx];
}
}
Comportamento por Build Mode
| Build Mode | Comportamento |
|---|---|
| Debug | Panic com stack trace |
| ReleaseSafe | Panic com stack trace |
| ReleaseFast | Comportamento indefinido |
| ReleaseSmall | Comportamento indefinido |
Em builds Release não-safe, a verificação de limites é removida para performance. Por isso é crucial testar em Debug.
Erros Relacionados
- buffer overrun — Estouro de buffer
- attempt to unwrap null optional — Acesso a optional nulo
- Segmentation fault — Acesso ilegal à memória