Для чего нужны ключевые слова any и some в Swift
Что проверяют
Понимаете ли вы разницу между непрозрачным и экзистенциальным типом, а не просто помните, что some пишут в SwiftUI. Следом обычно спрашивают, почему функция с some View не может вернуть в разных ветках разные вью и чем any дороже.
Как отвечать
Суть в том, кто знает конкретный тип:
some Protocol(opaque type): конкретный тип один и фиксирован, компилятор его знает, но скрывает от вызывающего кода.func makeView() -> some Viewобязан во всех ветках вернуть один и тот же тип, поэтому в SwiftUI для разных веток нужен@ViewBuilderилиAnyView. В параметреfunc f(_ x: some Collection)это короткая запись дженерика<T: Collection>;any Protocol(existential): коробка, в которой может лежать значение любого подходящего типа, причём тип может меняться во время работы. Нужен для разнородных коллекций вроде[any Shape]и для свойств, где реализацию подменяют.
Хронология, которую полезно знать: some для возвращаемого значения появился в Swift 5.1, явное any в Swift 5.6, а в 5.7 добавили some в параметрах, разрешили экзистенциалы для протоколов с associatedtype и ввели первичные ассоциированные типы, так что можно писать some Collection<Int>.
Цена any: значение хранится в экзистенциальном контейнере, большие значения уходят в кучу, методы вызываются через таблицу свидетелей протокола, и компилятор не может специализировать код. some и дженерики специализируются и работают быстрее. Практичное правило: по умолчанию some, а any только там, где действительно нужна разнородность.
Где спотыкаются
Говорят, что some значит «любой тип, подходящий под протокол». Это как раз описание any, а за some всегда стоит один конкретный тип.
Приводят [any Equatable] как рабочий пример. Он скомпилируется, но сравнить два элемента через == не получится: их конкретные типы могут различаться, а Equatable сравнивает только значения одного типа.
Уверены, что в Swift 6 код без any перед протоколом не соберётся. Обязательность явного any включается отдельным флагом ExistentialAny, по умолчанию её нет.