Механизм subscript в Swift появился вместе с языком в 2014 году и предназначен для доступа к данным коллекций более удобным синтаксисом. Subscripts позволили обращаться к элементам массивов, словарей и других коллекций через квадратные скобки. Современный Swift позволяет создавать пользовательские subscripts для собственных типов, делая API интуитивно понятным, как у стандартных структур данных.
Проблема: Раньше методы доступа выглядели более громоздкими (например, через .getElement(at:)), а subscript делает взаимодействие с типом лаконичным. Однако, неправильная реализация может привести к неоптимальному коду или ошибкам доступа за пределы коллекции.
Решение: Для реализации subscript используется ключевое слово subscript, после которого указываются параметры и возвращаемое значение. Subscript может быть как только для чтения, так и для чтения и записи. Кроме того, subscripts могут быть перегружены для разных параметров.
Пример кода:
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
Ключевые особенности:
Можно ли назначать subscript только для чтения? Или же subscript всегда должен быть get и set?
Subscript может быть только для чтения (только get), если в реализации отсутствует set. Не обязательно реализовывать оба аксессора, что аналогично свойствам.
Пример кода:
subscript(index: Int) -> Int { get { return index * 2 } }
Можно ли передавать изменяемые параметры через inout в subscript?
Нет, subscript не поддерживает inout-параметры в сигнатуре. Все параметры subscripts принимаются как let-константы внутри тела accessor-ов.
Могут ли subscripts возвращать опциональный тип?
Да, subscripts могут возвращать опциональные значения, что удобно, например, для безопасного доступа к элементам коллекции без риска выхода за пределы массива.
Пример кода:
extension Array { subscript(safe index: Int) -> Element? { return indices.contains(index) ? self[index] : nil } }
Реализация subscript без валидации на выход за границы для составного типа matrix, что приводит к краху приложения при ошибках с индексом.
Плюсы:
Минусы:
Добавлена опциональная версия subscript для безопасной индексации и обработки ошибок, что делает API явным и защищённым.
Плюсы:
Минусы: