TL;DR
Code của bạn đang truy cập Vec hoặc slice tại một chỉ số không tồn tại. Cách sửa an toàn nhanh nhất là thay vec[i] bằng vec.get(i), trả về Option thay vì gây crash.
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 5'
// Đoạn này panic khi i >= vec.len()
let val = vec[i];
// Đoạn này không bao giờ panic — tự xử lý trường hợp None
if let Some(val) = vec.get(i) {
println!("Got: {}", val);
} else {
eprintln!("Index {} out of bounds (len = {})", i, vec.len());
}
Nguyên nhân gốc rễ
Toán tử [] của Rust trên Vec và slice được thiết kế nghiêm ngặt. Truy cập ngoài phạm vi? Runtime sẽ panic ngay. Không có undefined behavior, không có memory corruption thầm lặng như trong C — chỉ là crash ngay lập tức và rõ ràng.
Một số tình huống thường xuyên gây ra lỗi này:
- Chỉ số hardcode chạy được lúc dev nhưng lỗi trên dữ liệu thực (lỗi kinh điển)
- Sai một đơn vị trong vòng lặp —
0..vec.len()thì ổn, còn0..=vec.len()vượt quá một phần tử - Tính chỉ số từ input của người dùng hoặc config mà không validate trước
- Slice một
Vecsau khi filter hoặc cắt bớt, nhưng quên cập nhật chỉ số cũ - Code đa luồng khi luồng khác thu nhỏ
Vecgiữa lúc tính chỉ số và lúc truy cập
Thông báo panic thực ra khá hữu ích:
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 5'
len is 3 nghĩa là các chỉ số hợp lệ là 0, 1, 2. Bạn đã thử dùng 5 — vượt quá hai phần tử so với cuối mảng.
Các cách sửa
Cách 1: Dùng .get() để truy cập an toàn
Chín trong mười trường hợp, đây là lựa chọn bạn muốn dùng trong production. .get(i) trả về Option<&T> — Some(&val) khi trong phạm vi, None khi không. Không bao giờ panic.
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()),
}
}
Cách này dùng được với mọi Vec<T> hoặc slice &[T] — không cần thay đổi gì tùy theo kiểu đang dùng.
Cách 2: Kiểm tra giới hạn tường minh trước khi truy cập
Đôi khi bạn thực sự cần dùng [] — để truy cập có thể thay đổi, cho rõ ràng, hoặc lý do khác. Chỉ cần thêm điều kiện kiểm tra:
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),
}
}
Tìm vị trí gây panic
Trước khi sửa, bạn cần tìm ra nó ở đâu. Đặt RUST_BACKTRACE=1 và chạy lại:
RUST_BACKTRACE=1 cargo run
Để xem chi tiết hơn — kể cả các frame được inline đôi khi ẩn nguyên nhân thực sự:
RUST_BACKTRACE=full cargo run
Dò backtrace để tìm tên crate của bạn. Bỏ qua các frame std:: và core:: ở đầu — đó là nội bộ Rust. Lỗi của bạn nằm ở các frame phía dưới chúng.
Slice range cũng gây panic
Cần biết thêm: index out of bounds cũng xảy ra khi slice với giới hạn cuối vượt phạm vi:
let v = vec![1, 2, 3];
let slice = &v[1..10]; // panic — 10 > len (3)
// Giới hạn end về phạm vi hợp lệ trước
let end = 10.min(v.len());
let slice = &v[1..end]; // an toàn
Xác nhận kết quả
Chạy lại sau khi sửa — không có output panic nghĩa là bạn đã xử lý đúng:
# Chạy bình thường
cargo run
# Chạy toàn bộ test
cargo test
Thêm regression test để lỗi không lén quay lại:
#[test]
fn test_out_of_bounds_handled() {
let data = vec![1, 2, 3];
assert!(data.get(5).is_none()); // ngoài giới hạn → None, không panic
assert_eq!(data.get(2), Some(&3)); // chỉ số hợp lệ cuối → Some
}
Để kiểm tra sâu hơn — đặc biệt trong unsafe code — Miri có thể phát hiện các vấn đề memory mà test thông thường bỏ sót:
cargo +nightly miri test
Tham khảo thêm
- Rust Reference — trait
SliceIndexvà hành vi panic Vec::getvàslice::gettrong tài liệu thư viện chuẩn- The Rustonomicon — đảm bảo an toàn bộ nhớ và lý do Rust panic thay vì undefined behavior
- Tài liệu biến môi trường
RUST_BACKTRACE

