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 は有効なインデックスが 0、1、2 であることを意味します。指定したのは 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::getとslice::get - The Rustonomicon — メモリ安全性の保証と Rust が未定義動作の代わりにパニックする理由
RUST_BACKTRACE環境変数のドキュメント

