---
title: "When to use a safe collection subscript"
description: "The extension turns an out-of-bounds crash into nil. That is useful when the index comes from outside your own code, and hides a logic error when it does not."
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 that is not random-access it resolves to `Sequence.contains`, which
walks from the start index — so `someString[safe: i]` is O(n) in the length of
the string, behind something that reads like a subscript. Constraining the
extension to `RandomAccessCollection` avoids that, at the cost of not having it
on `String`.

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