Volodymyr Sapsai

Indexing Sub-Sequences in Swift

December 31, 2024

For a long time, the following behavior in Swift was confusing to me:

let orig = Data([1, 2, 3, 4])
let sub = orig[1...2]
sub[0]
error: Execution interrupted.

For some reason, accessing the first element of the sub-sequence is considered to be out-of-bounds. But in C++ the following code is accepted:

std::vector orig{1, 2, 3, 4};
std::span sub(orig.begin() + 1, 2);
std::cout << sub[0] << std::endl;

And in JavaScript the snippet

orig = [1, 2, 3, 4];
sub = orig.slice(1, 3);
sub[0];

returns the value 2.

It was confusing to me until I saw a sentence

Indices from a slice can be used on the base collection

in Safe Access to Contiguous Storage proposal.

So it means that sub[0] refers not to the first element in the sub-sequence but to orig[0], which is indeed out-of-bounds for the sub-sequence. But what indices should we use with a sub-sequence? We have a sub-sequence for a reason; we don't want to work with the original sequence, don't want to know how its indices relate to the indices in the sub-sequence. To access the first element in the sub-sequence we can use Data.startIndex and make other indices relative to this one. Understandably, that is more verbose than just 0, but for common operations you don't have to write container[container.startIndex..<container.index(container.startIndex, offsetBy: 4, limitedBy: container.count)]. You can use a simpler container.prefix(4).

Given the new knowledge of how the sub-sequence indexing works, now the difference between Array.prefix(Int) and Array.prefix(upTo: Self.Index) is clearer. The [relative] number of an element in a sub-sequence is not the same as its index. So we have different methods for different cases. Now I find it extra useful to pay attention if the API works with indices or with the number of elements. When the underlying index type is Int it is tempting to treat both approaches as interchangeable, and it can work for a while. But it can start failing when you start working with sub-sequences.