index out of bounds — Como Resolver em Zig

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 ModeComportamento
DebugPanic com stack trace
ReleaseSafePanic com stack trace
ReleaseFastComportamento indefinido
ReleaseSmallComportamento indefinido

Em builds Release não-safe, a verificação de limites é removida para performance. Por isso é crucial testar em Debug.

Erros Relacionados

Continue aprendendo Zig

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