Subscripts

Subscripts let you query and set members of a collection, list, or sequence with concise square-bracket syntax — someArray[2] or someDictionary["key"]. Classes, structures, and enumerations can all define their own subscripts, giving your custom types the same natural access syntax as the built-in collections.

Subscript syntax

Define a subscript with the subscript keyword. It looks like a cross between an instance method and a computed property: you specify one or more input parameters and a return type, then provide a getter and an optional setter.

struct TimesTable {
    let multiplier: Int

    subscript(index: Int) -> Int {
        return multiplier * index
    }
}

let threeTimes = TimesTable(multiplier: 3)
print("6 × 3 is \(threeTimes[6])")   // → 6 × 3 is 18

A read-only subscript that computes a value from its index.

Like a read-only computed property, a subscript with only a getter can drop the get keyword and the explicit return for a single expression.

Subscript usage

"Subscript" is just the technical name for the bracket access you've used all along on arrays and dictionaries. Their behavior is defined by subscripts in the standard library.

var numbers = [10, 20, 30]
print(numbers[0])     // → 10 (getter)
numbers[1] = 25          // setter
print(numbers)        // → [10, 25, 30]

Subscript options

Subscripts can take any number of input parameters of any type, and can return any type. They can use variadic parameters and provide default parameter values, but they can't use inout parameters. A type can also provide multiple subscripts (subscript overloading), and Swift picks the right one based on the argument types.

Here's a read/write subscript that takes two parameters to index into a matrix stored as a flat array.

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]

    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }

    subscript(row: Int, column: Int) -> Double {
        get {
            grid[(row * columns) + column]
        }
        set {
            grid[(row * columns) + column] = newValue
        }
    }
}

var matrix = Matrix(rows: 2, columns: 2)
matrix[0, 1] = 1.5     // uses the setter
matrix[1, 0] = 3.2
print(matrix[0, 1])    // → 1.5 (uses the getter)

Tip: the setter implicitly receives the new value as newValue, exactly like a computed property's setter. You can give it your own name with set(newRow) if you prefer.

Subscripts on dictionaries

The Dictionary type's subscript takes a key and returns an optional value, because the key might not be present. Assigning nil through the subscript removes the key.

var ages = ["Anna": 28, "Brian": 35]

print(ages["Anna"])     // → Optional(28)
print(ages["Zoe"])      // → nil (key not present)

ages["Cara"] = 19      // add a new entry
ages["Brian"] = nil     // remove an entry
print(ages)            // → ["Anna": 28, "Cara": 19]

Note: this is why reading a dictionary value gives you an optional — the subscript itself returns Value?. Use optional binding or ?? to handle the missing-key case safely.

Type subscripts

Subscripts can belong to the type itself rather than an instance. Declare a type subscript with static before subscript (or class on a class to allow subclass overrides). You then call it on the type with bracket syntax.

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter

    static subscript(n: Int) -> Planet {
        return Planet(rawValue: n)!
    }
}

let third = Planet[3]
print(third)   // → earth

A static subscript maps a number to a planet through the type itself.

Recap

Subscripts give your types the clean bracket-access syntax of the built-in collections. You can make them read-only or read/write, accept multiple parameters, overload them, and even attach them to a type with static subscript. That wraps up the Functions & Types cluster — next you'll move on to inheritance and the object-oriented side of Swift.