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:
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:
subscript(index: Int) -> Int {
// Retrieve the value
}Example: A MultiplicationTable struct for a number's multiples.
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:
var animalSpeeds = ["cheetah": 100, "turtle": 1, "rabbit": 50]
animalSpeeds["eagle"] = 200This 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.
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.5This 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.
enum Color: Int {
case red = 1, green, blue, yellow
static subscript(code: Int) -> Color {
return Color(rawValue: code)!
}
}
let selectedColor = Color[2]
print(selectedColor) // greenThis fetches a color by its raw value directly on the enum type.