What is an ArraySlice, and why does slice[0] crash?

2.1.6 · L3 · Learning

Predict first

What does this print?

let a = [10, 20, 30, 40, 50]
let s = a[2...]
print(s.startIndex, s[2], s.first!)
  • 0 30 30
  • 2 30 30
  • 2 20 30

Predict first

What happens?

let a = [10, 20, 30, 40, 50]
let s = a[2...]
print(s[0])
  • Prints 10
  • Prints 30
  • Crashes

Mental model. A slice is a window cut into a long wall. The bricks keep their original numbers, and the wall stays standing.

A slice is a view into its parent array. Creating one is O(1) because it shares the parent’s buffer, and it keeps the whole parent alive for as long as the slice lives.

Crucially, a slice keeps the parent’s indices. a[2...] starts at index 2, so s[0] is out of range and traps. Use s.startIndex or s.first. Array(s) makes an independent array that starts at 0 and releases the parent. Data and Substring follow the same rule.

The slice starts at index 2 and still addresses the same elements, so s[2] is 30.

Index 0 is outside the slice’s range (2..<5), so it is an out-of-bounds trap.

Common traps

  • Indexing a slice from 0. Works by luck on prefix slices and crashes on dropFirst ones.
  • Keeping a small slice of a huge array. The whole buffer stays in memory. Copy it with Array(slice) if you keep it long.

They might follow up with

  • Which methods return slices? dropFirst, prefix, suffix, and range subscripts.
  • Is a slice a value type? Yes. Mutating a slice triggers copy-on-write like any array.

Related questions

  • 1.1.7 What is copy-on-write? Which types use it?
  • 2.3.5 What is Data, and how do you convert it to and from text?
  • 2.1.11 How do you write your own Collection, and what do you get for free?

← What happens if you mutate an array while you loop over it?   map, filter, compactMap, flatMap, reduce, forEach: when do you use… →