Skip to content

Subscripts

Subscripts provide a convenient way to access elements in collections, such as arrays, dictionaries, or custom types. They act like shortcuts for getting or setting values using index notation, like myCollection[index].

Classes, structs, and enums can define subscripts to handle access to their internal data. This avoids the need for separate getter and setter methods. For instance, you might access an array element with myArray[5] or a dictionary value with myDict["key"].

You can create multiple subscripts for the same type, and Swift chooses the right one based on the index type. Subscripts support multiple dimensions or parameters, fitting your type's requirements.

Syntax for Subscripts

Subscripts use square brackets for access and are defined with the subscript keyword. They include input parameters and a return type, similar to methods or computed properties.

For read-write subscripts, use a getter and optional setter:

swift
subscript(index: Int) -> Int {
    get {
        // Retrieve the value
    }
    set(newValue) {
        // Update the value
    }
}

The newValue in the setter matches the subscript's return type. If omitted, it defaults to newValue.

For read-only subscripts, simplify by dropping the get:

swift
subscript(index: Int) -> Int {
    // Retrieve the value
}

Example: A MultiplicationTable struct for a number's multiples.

swift
struct MultiplicationTable {
    let base: Int
    subscript(index: Int) -> Int {
        return base * index
    }
}

let fiveTable = MultiplicationTable(base: 5)
print("7 times 5 is \(fiveTable[7])")  // Prints "7 times 5 is 35"

Here, fiveTable[7] computes 5 * 7 = 35. It's read-only since direct assignment doesn't fit the fixed rule.

How to Use Subscripts

Subscripts' meaning varies by context but often shortcut collection access. Implement them to suit your type.

For example, Swift's dictionaries use subscripts for key-value operations:

swift
var animalSpeeds = ["cheetah": 100, "turtle": 1, "rabbit": 50]
animalSpeeds["eagle"] = 200

This adds or updates the "eagle" entry. Dictionary subscripts return optionals (Int?) to handle missing keys; set to nil to remove.

Advanced Subscript Features

Subscripts accept multiple parameters of any type and return any type. They support variadic or default parameters but not in-out.

Overload subscripts for different index types. Use multiple parameters for complex types.

Example: A Grid struct for a 2D array of numbers.

swift
struct Grid {
    let height: Int, width: Int
    var cells: [Double]
    
    init(height: Int, width: Int) {
        self.height = height
        self.width = width
        cells = Array(repeating: 0.0, count: height * width)
    }
    
    func isValid(row: Int, col: Int) -> Bool {
        return row >= 0 && row < height && col >= 0 && col < width
    }
    
    subscript(row: Int, col: Int) -> Double {
        get {
            assert(isValid(row: row, col: col), "Invalid position")
            return cells[(row * width) + col]
        }
        set {
            assert(isValid(row: row, col: col), "Invalid position")
            cells[(row * width) + col] = newValue
        }
    }
}

var myGrid = Grid(height: 3, width: 3)
myGrid[1, 2] = 4.5
myGrid[2, 1] = 6.0

print(myGrid[1, 2])  // 4.5

This sets values in a grid and checks bounds with assertions. Accessing invalid positions triggers an error.

Type-Level Subscripts

Subscripts can be static (or class-level), called on the type itself, not instances. Use static (or class for classes).

Example: An enum for colors by code.

swift
enum Color: Int {
    case red = 1, green, blue, yellow
    static subscript(code: Int) -> Color {
        return Color(rawValue: code)!
    }
}

let selectedColor = Color[2]
print(selectedColor)  // green

This fetches a color by its raw value directly on the enum type.

Released under the MIT License.