What goes wrong
An array or vector with n items has valid indexes 0 to n - 1. Any other index is invalid, and the most common bug when working with indexes is asking for one. The first index past the end, n, is the usual culprit: it feels natural, because n is the count of items, but it is one too large.
Rust never reads memory beyond the end of an array. It checks every index, and what happens next depends on whether the compiler can already see that the index is wrong.
Two ways the check fails
At compile time, for a fixed array and a constant index
A fixed-size array such as [50, 40, 30] has its length in its type: [i32; 3]. When you index it with a literal that is outside that length, the compiler can prove the access will fail, so it refuses to build the program:
let my_array = [50, 40, 30];
println!("{}", my_array[3]);
This produces the error this operation will panic at runtime with the note index out of bounds: the length is 3 but the index is 3. The program never starts. Negative literals are also compile errors: my_array[-1] is rejected with negative integers cannot be used to index, because indexes are usize values and can't be negative.
At run time, for everything else
When the index comes from a variable or a calculation, the compiler can't know its value. This includes every index into a Vec, whose length is only known while the program runs. The program builds, and the check happens when the line executes. A bad index makes the program panic and stop:
let my_array = [50, 40, 30];
let n = my_array.len();
let i = n + 27;
println!("{}", my_array[0]);
println!("{}", my_array[i]);
The first line prints 50. The second panics with index out of bounds: the len is 3 but the index is 30. A panic is an orderly shutdown with a message that names the length and the index, so the message tells you exactly what to compare.
Common causes
- Using the length as an index. With four items, the length is 4 but the last index is 3.
- An inclusive loop range.
for i in 0..=a.len()includesa.len(). The range0..a.len()stops one short, which is what you want. - Starting at 1. Counting items from 1 leads to asking for index
ninstead ofn - 1. - An empty collection. Index 0 is already invalid when there are no items.
Avoiding the panic
The method get checks the index for you and returns an Option: Some holding a reference to the item when it exists, and None when it doesn't.
let a = [50, 40, 30];
println!("{:?} {:?}", a.get(1), a.get(3));
Some(40) None
Use plain indexing when you are certain the index is valid, and get when it might not be. Indexes must also be usize: an i32 variable used as an index is a compile error, so convert it deliberately.
The lesson's exercise
The starter code builds a vector of four ages and prints the item at index 4:
let ages = vec![14, 32, 50, 21];
println!("{}", ages[4]);
The goal is to print the last element, 21. Running the starter code panics with index out of bounds: the len is 4 but the index is 4. The message already contains the diagnosis: there are 4 items, so the valid indexes are 0 to 3, and index 4 is one past the end. The last item is at index 3:
let ages = vec![14, 32, 50, 21];
println!("{}", ages[3]);
This prints 21. Writing the index as a literal works here because the vector never changes. When the length can vary, compute the index as ages.len() - 1, as the next lesson shows.
Key takeaways
- Valid indexes run from 0 to length minus 1.
- An out-of-range literal index into a fixed array is a compile error; an out-of-range index computed at run time, or any bad index into a vector, panics.
- Read the panic message: it states the length and the index you used.
- Use
getto receive anOptioninstead of a panic.
