Valid and invalid indices
An array of n elements has the indices 0 to n - 1. Any other index is outside the array's bounds. This is the most common mistake when working with arrays, and Go does not let it slip by: reading or writing outside the bounds is always an error. It never quietly returns some leftover value from memory.
Go reports the error at one of two moments, depending on what the index is.
A constant index: a compile error
The length of an array is part of its type, so the compiler knows it. If the index is a constant too, the compiler can check it before the program runs and refuses to build:
myArray := [3]int{50, 40, 30}
fmt.Println(myArray[2]) // fine: 30
fmt.Println(myArray[3]) // compile error
fmt.Println(myArray[-1]) // compile error
The messages are invalid argument: index 3 out of bounds [0:3] and invalid argument: index -1 (constant of type int) must not be negative. Because the build stops, no part of the program runs, not even the lines before the mistake.
Go treats len of an array as a constant too. That is why the lesson's starting code fails to compile:
ages := [4]int{14, 32, 50, 21}
fmt.Println(ages[len(ages)])
invalid argument: index 4 out of bounds [0:4]
The value of len(ages) is 4, and the last valid index is 3. The compiler spotted this before anything ran.
A variable index: a run-time panic
When the index comes from a variable, the compiler cannot know its value, so the check happens while the program runs. An out-of-range index then triggers a panic, which stops the program with a message:
ages := [4]int{14, 32, 50, 21}
k := 4
fmt.Println(ages[k])
panic: runtime error: index out of range [4] with length 4
The message names the bad index and the length, followed by a trace of where it happened. Read it first when you debug: it says exactly which index was wrong.
The exercise
The starting code tries to print the last element:
ages := [4]int{14, 32, 50, 21}
fmt.Println(ages[len(ages)])
It must print 21. The mistake is the index. Counting starts at 0, so the four elements sit at 0, 1, 2 and 3, and len(ages) is one too large. Subtract 1:
ages := [4]int{14, 32, 50, 21}
fmt.Println(ages[len(ages)-1])
21
Writing ages[3] would print the same thing, but len(ages) - 1 keeps working if the array's size changes later, so it is the better habit.
Staying inside the bounds
A few habits keep your indices valid, and they cost almost nothing to follow:
- Use
len(array) - 1for the last index instead of a hard-coded number. - In a counting loop, use
i < len(array), noti <= len(array). - Prefer
for i := range array, which only produces valid indices. - Check a computed index with an
ifbefore using it:if k >= 0 && k < len(ages).
A guard in practice
When an index comes from outside your code, test it before you use it. This loop tries three indices, and only the valid one reads the array:
for _, k := range []int{2, 4, -1} {
if k >= 0 && k < len(ages) {
fmt.Println(k, ages[k])
} else {
fmt.Println(k, "out of range")
}
}
2 50
4 out of range
-1 out of range
The condition checks both ends of the range, because a negative index is as invalid as one that is too large.
Common mistakes
- Treating the length as the last index. This is the off-by-one in the exercise.
- Expecting a negative index to count from the end. Go has no such feature, and a constant negative index is a compile error.
- Trusting a computed index. An index that comes from arithmetic, such as
i + 1at the last position of a loop, is the usual source of a run-time panic. - Writing past the end. Assigning to
ages[4]breaks the same rule as reading it. An array never grows. - Expecting a garbage value. Go does not hand back unrelated memory for a bad index. You get either a compile error or a panic.
Key takeaways
- Valid indices are 0 to
len(array) - 1. - A constant index outside the array is rejected by the compiler.
- A variable index outside the array makes the program panic when it runs.