The subscript

Subscripts can be defined in classes, structures, and enumerations, and are shortcuts to accessing elements in collections, lists, or sequences. You can use the index of the subscript to set and get values without calling the corresponding access method. For example, using subscripts to access elements in an Array instance can be written as someArray[index], or accessing elements in a Dictionary instance can be written as someDictionary[key].

A type can define multiple subscripts, which can be overloaded by different index types. Subscripts are not limited to one dimension, you can define subscripts with multiple input parameters to meet the needs of custom types.


The subscript grammar

Subscripts allow you to access an instance by passing in one or more index values in square brackets following the instance name. The syntax is similar to a mix of instance method syntax and computational attribute syntax. Like defining instance methods, subscripts are defined using the subscript keyword, specifying one or more input parameters and return types; Unlike instance methods, subscripts can be set to read/write or read-only. This behavior is implemented by getters and setters, and is similar to computational properties:

subscript(index: Int) - >Int {
    get {
      // Return an appropriate value of type Int
    }
    set(newValue) {
      // Perform the appropriate assignment}}Copy the code

NewValue has the same type as the return type of the subscript. As with computational properties, you do not specify setter arguments (newValue). If no argument is specified, the setter provides a default argument named newValue. As with read-only computational properties, you can omit the read-only subscript get keyword:

subscript(index: Int) - >Int {
    // Return an appropriate value of type Int
}
Copy the code

The following code demonstrates the implementation of read-only subscripts, where a TimesTable is defined to represent the multiplication table of passed integers:

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) - >Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// Print "six times three is 18"
Copy the code

In the above example, an instance of TimesTable is created to represent the TimesTable of the integer 3. The value 3 is passed to the constructor of the structure as the value of the instance member Multiplier.

In the above example, an instance of TimesTable is created to represent the TimesTable of the integer 3. The value 3 is passed to the constructor of the structure as the value of the instance member Multiplier.

You can access instances of threeTimesTable by subscript, such as threeTimesTable[6] demonstrated above. This query queries the sixth element in the multiplication table of 3, returning 6 times 3, which is 18.

Note that the TimesTable example is based on a fixed mathematical formula and it is not appropriate to assign threeTimesTable[someIndex], so the subscript is defined as read-only.


The subscript usage

The exact meaning of the subscript depends on the usage scenario. Subscripts are often used as shortcuts to access elements in a collection, list, or sequence. You can implement subscripts in the most appropriate way for the functionality of your particular class or structure.

For example, Swift’s Dictionary type implements subscripts to access values stored in instances. To set a dictionary, use a key of the same type as the dictionary’s key in the subscript and assign a value of the same type as the dictionary’s value to the subscript:

var numberOfLegs = ["spider": 8."ant": 6."cat": 4]
numberOfLegs["bird"] = 2
Copy the code

The above example defines a variable named numberOfLegs and initializes it with a dictionary literal containing three pairs of key values. The type of numberOfLegs dictionary is inferred to be [String: Int]. Once the dictionary is created, this example adds the key bird of type String and the value 2 of type Int to the dictionary by subscript.

For more information about Dictionary subscripts, see Reading and Modifying dictionaries.

Pay attention to

Swift’s dictionary-type subscript accepts and returns a value of an optional type. The numberOfLegs dictionary in the example above returns an Int? Or “optional int”. The Dictionary type implements subscripts this way because not every key has a value, and it also provides a way to delete the value by key, simply by assigning the key value to nil.


The subscript options

  • Subscripts can accept any number of inputs, and these inputs can be of any type.
  • The return value of the subscript can also be of any type.
  • Subscripts can use variable parameters and can provide default parameter values, but cannot use input/output parameters.

A class or structure can provide multiple subscripts based on its own needs. Subscripts are automatically matched by the number and type of input parameters. This is subscript overloading.

While subscripts that accept a single input parameter are the most common, subscripts that accept more than one input parameter can also be defined depending on the situation. For example, the following example defines a Matrix structure that represents a two-dimensional Matrix of type Double. The subscript of the Matrix structure accepts two integer parameters:

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)
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) - >Double {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

Copy the code

Matrix provides a constructor that takes two input arguments, rows and columns, and creates an array large enough to hold a value of type Double in rows * columns. Initialize the value of each position in the matrix to 0.0 by passing the array length and an initial value of 0.0 to the array’s constructor. See Creating an Array with default values for this constructor of arrays.

You can construct a new Matrix instance by passing in the appropriate number of rows and columns:

var matrix = Matrix(rows: 2, columns: 2)
Copy the code

In the above example, an instance of Matrix is created to represent a two-row, two-column Matrix. The grid array of the Matrix instance stores the Matrix flat in reading order from top left to bottom right:

Set the matrix by passing the values of row and column to subscripts separated by commas:

matrix[0.1] = 1.5
matrix[1.0] = 3.2
Copy the code

The above two statements call the subscript setter to set the upper-right corner of the matrix (row 0, column 1) to 1.5 and the lower-left corner of the matrix (row 1, column 0) to 3.2, respectively

The getter and setter of the Matrix subscript contain assertions that check whether the values of the subscript arguments row and column are valid. To facilitate assertions, Matrix contains a convenient method called indexIsValid(Row: Column 🙂 that checks whether the values of the row and column inputs are in the range of the Matrix:

func indexIsValid(row: Int, column: Int) -> Bool {
    return row >= 0 && row < rows && column >= 0 && column < columns
}
Copy the code

Assertions are emitted when subscripts are out of bounds:

let someValue = matrix[2.2]
// The assertion will fire because [2, 2] is out of range of matrix

Copy the code

Type the subscript

As described in the previous section, instance subscripts are subscripts called on an instance of a particular type. You can also define a subscript that is called on the type itself. This type of subscript is called a type subscript. You can indicate a type subscript by writing the static keyword before the subscript keyword. Classes can use the class keyword to allow subclasses to override the implementation of that subscript in their parent class. The following example shows how to define and invoke a type subscript:

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
    static subscript(n: Int) - >Planet {
        return Planet(rawValue: n)! }}let mars = Planet[4]
print(mars)
Copy the code