---
title: "When to use a safe collection subscript"
description: "Should an out-of-bounds index crash, or come back nil? It turns on whether you produced the index or someone else did — plus one performance trap on collections that are not arrays."
author: "Omar Albeik"
date: 2019-07-12
type: note
topics: [swift, api-design, software, engineering]
language: en
reading_time_minutes: 2
canonical_url: https://omaralbeik.com/en/blog/safe-collection-subscript
translation_url: https://omaralbeik.com/ar/blog/safe-collection-subscript
source_url: https://omaralbeik.com/en/blog/safe-collection-subscript.md
---

# When to use a safe collection subscript

The extension is four lines, and most Swift codebases have it somewhere:

```swift
extension Collection {
  subscript(safe index: Index) -> Element? {
    indices.contains(index) ? self[index] : nil
  }
}
```

```swift
let names = ["John", "Mike"]

names[2]        // crash
names[safe: 2]  // nil
```

An out-of-bounds subscript is one of the few things in Swift that terminates
the process, and this converts it into an optional that `if let` can handle.

The tradeoff is where the failure surfaces. `names[2]` on a two-element array
means some earlier code produced an index that does not exist. Swift traps at
that subscript, so the offending index and the call stack that produced it are
both in front of you. With `[safe:]` you get `nil`, the branch does nothing,
and the visible symptom is an empty row or a total that is short by one —
somewhere else, later.

So the question is whether you know the index is valid. If you computed it from
`count`, or you are iterating `indices`, you do, and the optional adds a branch
for a case that cannot happen. If the index came from elsewhere, you do not:

- an index parsed from a payload or a URL
- a selection captured before a reload and used after it
- an index passed back by a framework callback for a collection you have since
  mutated

In all three the collection and the index come from different code paths, which
is the thing to check for.

One implementation detail. `indices.contains(index)` is a range check on
`Array`, where `indices` is a `Range<Int>` and `contains` is O(1). On a
collection whose indices use `Sequence.contains`, membership requires a
linear scan. Restricting the extension to `RandomAccessCollection` does not
by itself guarantee a constant-time `indices.contains` implementation. Check
the concrete collection if this subscript sits on a hot path.

Use it where the index is untrusted. Elsewhere, the trap is more useful than
the optional.

Where do you draw the line between returning `nil` and letting an invalid
index fail? [Send me an example](/en/contact) from your own code. If this
distinction comes up in a code review, share this note with the team.
