The bounds of an array
An array's items are indexed from 0 to count - 1. This range is the array's bounds, and any index outside it is invalid. Exceeding the bounds is the most common problem when using indexes.
var ourArray = [50, 40, 30]
// Valid indexes: 0, 1, 2
print(ourArray[0]) // 50
print(ourArray[2]) // 30
For this array the indexes 3 and 30 are past the end and -1 is before the start. Swift does not read from the end when you give it a negative number.
What Swift does
Swift checks every array access. When the index is invalid, it stops the program with a fatal error, usually described as "Index out of range". The error is not thrown in the sense of throw and try, so a do/catch block can't handle it. The program ends there, and the lines after the failing one don't run. This is a safety feature: the program never reads memory that doesn't belong to the array.
The same happens when you assign to an invalid index. A subscript only replaces an element that exists, so ourArray[3] = 5 on a three-element array crashes. Use append to add an element.
The exercise: find the mistake
This program should print the last element of ages, which is 21:
var ages = [14, 32, 50, 21]
print(ages[ages.count])
Instead it crashes. To find the bug, look at the index on the line that prints.
- Evaluate the index.
ages.countis 4, so the code readsages[4]. - List the valid indexes. Four elements occupy indexes 0, 1, 2 and 3. Index 4 doesn't exist.
- Move one step back. The last element is at
ages.count - 1.
var ages = [14, 32, 50, 21]
print(ages[ages.count - 1])
This prints 21.
Avoiding the crash
The mistake is easy to repeat with an index that is computed, so check it first:
let index = 4
if index < ages.count {
print(ages[index])
} else {
print("out of range") // this branch runs
}
Also test for a negative index, or use ages.indices.contains(index), which checks both ends. The first and last properties return optionals and give nil on an empty array, so they never crash. Loops written as for i in 0..<ages.count stay within the bounds, because the upper bound of ..< is excluded.
Reading a crash report
A crash like this is easy to fix once you know where to look. The failing line is the one that contains a subscript, so start with the number between the brackets and work out its value. If it comes from a variable or a calculation, evaluate it by hand with the array's real size. Then compare the result with the range 0 to count - 1. In most cases the index is off by one, or the array is shorter than the code assumed, for example because it is empty or because items were removed earlier.
Common mistakes
- Treating the count as an index. With four elements, 4 is a count, and the last index is 3.
- Using
...in a loop.0...ages.countincludes 4. Use0..<ages.count. - Expecting negative indexes to work.
ages[-1]crashes. - Empty arrays. Nothing is a valid index there, and
count - 1is -1. - Forgetting that removing shrinks the array. After
remove(at:), the indexes you used before may no longer exist.
Key takeaways
- Valid indexes go from 0 to
count - 1. - An invalid index stops the program with a fatal error, and it can't be caught.
- Use
array[array.count - 1]for the last element. - Validate indexes that come from variables.
