Rustのパニックを修正: index out of bounds — len は 3 だがインデックスは 5

beginner🦀 Rust2026-03-25| Rust 1.60以降、全プラットフォーム(Linux、macOS、Windows)

Error Message

thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 5'
#rust#パニック#vec#スライス#インデックス範囲外

TL;DR

コードが存在しないインデックスで Vec またはスライスにアクセスしています。最も手軽で安全な修正は、vec[i]vec.get(i) に置き換えることです。これはパニックを起こす代わりに Option を返します。

thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 5'
// i >= vec.len() のときにパニックする
let val = vec[i];

// パニックしない — None の場合は自分で処理する
if let Some(val) = vec.get(i) {
    println!("Got: {}", val);
} else {
    eprintln!("Index {} out of bounds (len = {})", i, vec.len());
}

原因

Rust の Vec とスライスに対する [] 演算子は、設計上厳格です。範囲外アクセスが発生すると、ランタイムがパニックします。C のような未定義動作やサイレントなメモリ破壊はなく、即座に明確なクラッシュが起きます。

この問題が確実に発生するいくつかの状況:

  • 開発時は動いていたハードコードされたインデックスが、実データで壊れる(よくあるケース)
  • ループでの 1 つずれ — 0..vec.len() は正しいが、0..=vec.len() は末尾の 1 つ先まで進む
  • ユーザー入力や設定からインデックスを計算する際に、事前検証を忘れた場合
  • フィルタリングや切り詰めの後に Vec をスライスしたが、古いインデックスの更新を忘れた場合
  • マルチスレッドのコードで、インデックス計算とアクセスの間に別スレッドが Vec を縮小した場合

パニックメッセージは実際にかなり参考になります:

thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 5'

len is 3 は有効なインデックスが 012 であることを意味します。指定したのは 5 — 末尾から 2 つ先です。

修正方法

方法 1: .get() を使った安全なアクセス

本番環境では、ほとんどの場合これが最適です。.get(i)Option<&T> を返します — 範囲内なら Some(&val)、範囲外なら None。パニックは絶対に起きません。

fn print_element(data: &[i32], index: usize) {
    match data.get(index) {
        Some(val) => println!("data[{}] = {}", index, val),
        None => eprintln!("ERROR: index {} out of bounds (len = {})", index, data.len()),
    }
}

これは任意の Vec<T> または &[T] スライスでコンパイルできます — どちらを使っているかによって変更は不要です。

方法 2: インデックスアクセス前に明示的な境界チェックを行う

可変アクセスや可読性などの理由で、どうしても [] が必要な場合もあります。その場合はガードを追加してください:

let data = vec![10, 20, 30];
let i = 5;

if i  Result {
    data.get(index)
        .copied()
        .ok_or_else(|| format!("index {} out of bounds (len = {})", index, data.len()))
}

fn main() {
    let data = vec![1, 2, 3];
    match get_element(&data, 5) {
        Ok(val) => println!("Got {}", val),
        Err(e) => eprintln!("Error: {}", e),
    }
}

パニックの発生箇所を特定する

修正する前に、まず問題箇所を見つける必要があります。RUST_BACKTRACE=1 を設定して再実行してください:

RUST_BACKTRACE=1 cargo run

真の原因が隠れていることのあるインライン展開されたフレームも含め、さらに詳細な情報を得るには:

RUST_BACKTRACE=full cargo run

バックトレースで自分のクレート名を探してください。先頭にある std::core:: のフレームはスキップしてください — それらは Rust の内部実装です。バグはその下のフレームにあります。

スライスの範囲指定もパニックを引き起こす

知っておくべき点として、範囲外の終端を指定したスライスでも index out of bounds が発生します:

let v = vec![1, 2, 3];
let slice = &v[1..10]; // パニック — 10 > len (3)

// 先に終端を有効な範囲に制限する
let end = 10.min(v.len());
let slice = &v[1..end]; // 安全

確認方法

修正後に再実行してください — パニック出力がなければ成功です:

# クリーン実行
cargo run

# テストスイート全体
cargo test

再発を防ぐためにリグレッションテストを追加しましょう:

#[test]
fn test_out_of_bounds_handled() {
    let data = vec![1, 2, 3];
    assert!(data.get(5).is_none());      // 範囲外 → パニックではなく None
    assert_eq!(data.get(2), Some(&3));   // 最後の有効インデックス → Some
}

特に unsafe コードにおける深い検証には、Miri を使うとテストでは検出できないメモリの問題を見つけられます:

cargo +nightly miri test

参考資料

  • Rust リファレンス — SliceIndex トレイトとパニックの挙動
  • 標準ライブラリドキュメントの Vec::getslice::get
  • The Rustonomicon — メモリ安全性の保証と Rust が未定義動作の代わりにパニックする理由
  • RUST_BACKTRACE 環境変数のドキュメント

Related Error Notes