Where is my Index At?

A short post about accessing arrays safely.

Β·

3 min read

How about a small Swift riddle to start your day?
What is the matter with this code? The error "Index out of bounds" is a bit unexpected since the array is not empty and we try to access the first element, or we are not accessing the first element?

Accessing the wrong index of an array is probably one of the most common and dangerous mistakes a developer can make. But we just checked and our array isn't empty. Therefore why it is not safe to access the index zero of the array?
This seems counterintuitive at first, but we should not assume anything! Only because an array is not empty, it does not mean that it will be possible to access its values starting at the zero position.

The short answer: The function dropFirst() returns a type ArraySlice which maintains the indices of the original array, the one being sliced!

Read below for more about this or watch the short video.

Explanation

dropFirst() from Swift.Collection.Array returns a type of ArraySlice as in the documentation pages:

@inlinable public func dropFirst( _ k: Int = 1) -> ArraySlice

The ArraySlice type makes it fast and efficient to perform operations on sections of a larger array. Instead of copying over the elements of a slice to new storage, an ArraySlice instance presents a view of the storage of a larger array. And because ArraySlice presents the same interface as Array, you can generally perform the same operations on a slice as you could on the original array.

Slices maintain indices!

Unlike Array and ContiguousArray, the starting index for an ArraySlice instance isn’t always zero. Slices maintain the same indices of the larger array for the same elements, so the starting index of a slice depends on how it was created, letting you perform index-based operations on either a full array or a slice.
The above code returns a slice of value [2,3] but the index did not change.

This:

let n = new[1]

would give the expected result accessing the first element of our slice. Apple recommends to always use the startIndex and endIndex properties instead of specific values, to safely reference the starting and ending indices of a slice.

Arrays and ArraySlice have a startIndex and endIndex property:

new.startIndex // The starting index of our slice is 2

For instance, to be safe, our code could be better written as:

let n = new[startIndex]

ArraySlice is temporary storage.

As per the Apple documentation:

Long-term storage of ArraySlice instances is discouraged. A slice holds a reference to the entire storage of a larger array, not just to the portion it presents, even after the original array’s lifetime ends. Long-term storage of a slice may therefore prolong the lifetime of elements that are no longer otherwise accessible, which can appear to be memory and object leakage.

In conclusion:

Be careful when you access arrays. So you know. ArraySlice references the indices of our original array enabling our code to be more performant.

There is a small video here.