Start learning free

LESSON 104 OF 414 · ARRAYS I

Exceeding Static Array Bounds in Rust: Panics and Compile Errors

Lesson Read the full explanation

When accessing array data with indices, the most common problem we can run into is exceeding the array's bounds.

Remember, the items of an array are indexed from 0 to length - 1. Any index which is not in this range is invalid.

When you try to access an item with an invalid index, Rust reports an index out of bounds error. With a constant index on an array, as below, the compiler rejects the program (this operation will panic at runtime); with an index computed while the program runs, it panics with index out of bounds instead:

let my_array = [50, 40, 30];

// Valid indices: 0, 1, 2
println!("{}", my_array[0]); // Output: 50
println!("{}", my_array[2]); // Output: 30

// Invalid indices: 3, 30
println!("{}", my_array[3]); // compile error: index out of bounds
println!("{}", my_array[30]); // compile error: index out of bounds
// Note: negative indices are a compile-time error in Rust

As programmers, we always want to make sure that we don't exceed one array's bounds in our programs.


Assignment
Follow the Coding Tutorial and let's play with some arrays.


Hint
Look at the examples above if you get stuck.


STEP 1 OF 1
    Ctrl + Enter to check

    RUST · ARRAYS I

    Exceeding Static Array Bounds in Rust, explained

    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() includes a.len(). The range 0..a.len() stops one short, which is what you want.
    • Starting at 1. Counting items from 1 leads to asking for index n instead of n - 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 get to receive an Option instead of a panic.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free