Panic: Index Out of Bounds no Rust

Como evitar o panic de index out of bounds no Rust ao acessar arrays e vetores. Aprenda a usar get(), iteradores, bounds checking e outras técnicas seguras.

Panic: Index Out of Bounds

O panic “index out of bounds” ocorre quando você tenta acessar um elemento de um array, slice ou Vec usando um índice que está fora do intervalo válido. É um erro de runtime que causa o encerramento imediato do programa.

A Mensagem de Erro

thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 5', src/main.rs:4:20
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Variação com slices:

thread 'main' panicked at 'range end index 10 out of range for slice of length 3', src/main.rs:4:22

O Que Significa

Arrays, slices e Vec no Rust são indexados a partir de 0. Um vetor com 3 elementos tem índices válidos 0, 1 e 2. Acessar o índice 3 (ou qualquer índice >= comprimento) causa panic.

O Rust faz bounds checking (verificação de limites) em todo acesso por índice. Isso é diferente de C/C++, onde acessar fora dos limites causa comportamento indefinido silenciosamente. No Rust, o programa aborta com uma mensagem clara em vez de continuar com dados corrompidos.

Cenários comuns:

  1. Loop com índice erradofor i in 0..=len (deveria ser 0..len)
  2. Vetor vazio — acessar vec[0] sem verificar se está vazio
  3. Off-by-one — erro clássico de “um a mais” ou “um a menos”
  4. Índice calculado — resultado de cálculo que excede o comprimento

Código com Erro

fn main() {
    let numeros = vec![10, 20, 30];

    // PANIC: índice 3 não existe (válidos: 0, 1, 2)
    println!("{}", numeros[3]);

    // PANIC: vetor pode estar vazio
    let vazio: Vec<i32> = Vec::new();
    println!("{}", vazio[0]);

    // PANIC: off-by-one no loop
    for i in 0..=numeros.len() {  // =numeros.len() está fora
        println!("{}", numeros[i]);
    }

    // PANIC: slice fora dos limites
    let fatia = &numeros[1..5];
}

Como Resolver

Solução 1: Usar .get() para Acesso Seguro

O método .get() retorna Option<&T>None se o índice for inválido:

fn main() {
    let numeros = vec![10, 20, 30];

    // Seguro: retorna None se não existir
    match numeros.get(5) {
        Some(valor) => println!("Valor: {}", valor),
        None => println!("Índice inválido"),
    }

    // Com unwrap_or para valor padrão
    let valor = numeros.get(5).unwrap_or(&0);
    println!("Valor: {}", valor);

    // if let para caso simples
    if let Some(primeiro) = numeros.get(0) {
        println!("Primeiro: {}", primeiro);
    }
}

Para slices, use .get(range):

fn main() {
    let numeros = vec![10, 20, 30];

    // Seguro: retorna None se o range for inválido
    if let Some(fatia) = numeros.get(1..5) {
        println!("{:?}", fatia);
    } else {
        println!("Range inválido");
    }
}

Solução 2: Usar Iteradores em Vez de Índices

Iteradores eliminam completamente o risco de index out of bounds:

fn main() {
    let numeros = vec![10, 20, 30, 40, 50];

    // Em vez de for i in 0..numeros.len(), use:
    for numero in &numeros {
        println!("{}", numero);
    }

    // Com índice se necessário
    for (i, numero) in numeros.iter().enumerate() {
        println!("[{}] = {}", i, numero);
    }

    // Processar pares de elementos adjacentes
    for par in numeros.windows(2) {
        println!("{} -> {}", par[0], par[1]);
    }

    // Processar em chunks
    for grupo in numeros.chunks(2) {
        println!("{:?}", grupo);
    }
}

Solução 3: Verificar Limites Antes de Acessar

fn main() {
    let numeros = vec![10, 20, 30];
    let indice = 5;

    // Verificação explícita
    if indice < numeros.len() {
        println!("{}", numeros[indice]);
    } else {
        println!("Índice {} fora dos limites (max: {})", indice, numeros.len() - 1);
    }

    // Para primeiro/último elemento
    if let Some(primeiro) = numeros.first() {
        println!("Primeiro: {}", primeiro);
    }

    if let Some(ultimo) = numeros.last() {
        println!("Último: {}", ultimo);
    }

    // Verificar se não está vazio
    if !numeros.is_empty() {
        println!("Primeiro: {}", numeros[0]);
    }
}

Solução 4: Usar first(), last() e Métodos Seguros

fn main() {
    let numeros = vec![10, 20, 30];

    // Métodos seguros que retornam Option
    let primeiro = numeros.first();     // Some(&10)
    let ultimo = numeros.last();        // Some(&30)

    // Para vetores mutáveis
    let mut dados = vec![1, 2, 3];
    if let Some(primeiro) = dados.first_mut() {
        *primeiro = 100;
    }
    println!("{:?}", dados); // [100, 2, 3]

    // pop() remove e retorna o último elemento
    let removido = dados.pop(); // Some(3)
    println!("Removido: {:?}", removido);
}

Solução 5: Usar saturating_sub para Evitar Underflow em Índices

fn main() {
    let numeros = vec![10, 20, 30, 40, 50];
    let posicao: usize = 1;

    // Pegar elementos ao redor de uma posição
    let inicio = posicao.saturating_sub(2); // Não vai abaixo de 0
    let fim = (posicao + 3).min(numeros.len()); // Não passa do comprimento

    let vizinhos = &numeros[inicio..fim];
    println!("Vizinhos: {:?}", vizinhos);
}

Padrão Comum: Processar Entrada do Usuário

fn buscar_item(lista: &[String], indice_str: &str) -> Option<&String> {
    let indice: usize = indice_str.parse().ok()?;
    lista.get(indice)
}

fn main() {
    let itens = vec![
        String::from("maçã"),
        String::from("banana"),
        String::from("laranja"),
    ];

    // Simula input do usuário
    let entrada = "1";

    match buscar_item(&itens, entrada) {
        Some(item) => println!("Item: {}", item),
        None => println!("Item não encontrado"),
    }
}

Comparação de Abordagens

AbordagemPanic?Uso recomendado
vec[i]Sim, se foraQuando o índice é garantido válido
vec.get(i)NaoAcesso com índice de fonte externa
for item in &vecNaoIterar sobre todos os elementos
vec.first() / vec.last()NaoAcessar extremidades
if i < vec.len()NaoVerificação manual

Veja Também