The subscript mechanism in Swift was introduced along with the language in 2014 and is designed for accessing data collections with a more convenient syntax. Subscripts allow accessing elements of arrays, dictionaries, and other collections using square brackets. Modern Swift permits the creation of custom subscripts for user-defined types, making the API intuitive, similar to standard data structures.
Problem: Previously, accessor methods looked more cumbersome (for example, through .getElement(at:)), while subscript simplifies the interaction with the type. However, improper implementation can lead to suboptimal code or access errors outside the collection.
Solution:
To implement a subscript, the keyword subscript is used, followed by parameters and the return value. A subscript can be read-only or read and write. Moreover, subscripts can be overloaded for different parameters.
Example code:
struct Matrix { let rows: Int let columns: Int var grid: [Double] init(rows: Int, columns: Int) { self.rows = rows self.columns = columns self.grid = Array(repeating: 0.0, count: rows * columns) } subscript(row: Int, column: Int) -> Double { get { assert(row >= 0 && row < rows && column >= 0 && column < columns, "Index out of range") return grid[(row * columns) + column] } set { assert(row >= 0 && row < rows && column >= 0 && column < columns, "Index out of range") grid[(row * columns) + column] = newValue } } } var matrix = Matrix(rows: 2, columns: 2) matrix[0, 1] = 3.14 print(matrix[0, 1]) // 3.14
Key features:
Can you assign a read-only subscript? Or does a subscript always have to have both get and set?
A subscript can be read-only (only get) if the implementation lacks set. It is not necessary to implement both accessors, which is similar to properties.
Example code:
subscript(index: Int) -> Int { get { return index * 2 } }
Can you pass mutable parameters through inout in a subscript?
No, a subscript does not support inout parameters in the signature. All parameters of subscripts are treated as let constants within the accessor body.
Can subscripts return an optional type?
Yes, subscripts can return optional values, which is convenient, for instance, for safely accessing elements of a collection without the risk of going out of bounds.
Example code:
extension Array { subscript(safe index: Int) -> Element? { return indices.contains(index) ? self[index] : nil } }
Implementation of a subscript without boundary validation for a composite type matrix, leading to application crashes when index errors occur.
Pros:
Cons:
Added an optional version of the subscript for safe indexing and error handling, making the API explicit and protected.
Pros:
Cons: