Что такое прозрачные и непрозрачные типы в Swift
Что проверяют
Понимаете ли вы, кто выбирает конкретный тип и кто его видит: вызывающий код или вызываемый. Почти всегда следом спрашивают, чем some отличается от any и зачем в SwiftUI пишут var body: some View.
Как отвечать
Начните с определения: прозрачный тип вызывающий код видит целиком, непрозрачный скрывает конкретный тип от вызывающего, но не от компилятора.
- прозрачный тип: функция возвращает
[Int]или конкретную структуру, а в дженерикеfunc draw<T: Shape>(_ s: T)типTвыбирает вызывающий; - непрозрачный тип
some Shape: тип выбирает сама функция, снаружи виден только протокол. Компилятор знает конкретный тип, поэтому сохраняется идентичность типа, работают associated types иEquatable, возможны специализация и статический вызов. Для возвращаемых значений появился в Swift 5.1, с Swift 5.7someразрешён и в параметрах; - экзистенциальный тип
any Shape: коробка, в которой во время выполнения лежит значение любого подходящего типа. Вызовы идут через witness table, крупные значения уходят в кучу, информация о конкретном типе теряется; - type erasure: ручные обёртки вроде
AnyView,AnyPublisher,AnyHashable, которыми закрывали дыру, пока протоколы с associated types нельзя было использовать как тип.
Главное ограничение some: из всех веток функция возвращает один и тот же конкретный тип. Поэтому body в SwiftUI помечен @ViewBuilder, который собирает if/else в один тип _ConditionalContent. Настоящий тип body выглядит как VStack<TupleView<(Text, Image)>>, и some View избавляет от необходимости его выписывать.
Где спотыкаются
Путают some и any. Непрозрачный тип это один фиксированный тип, скрытый от читателя кода, экзистенциальный это разные типы в одной коробке с динамической диспетчеризацией.
Называют протоколы с associated types «прозрачными типами». Прозрачность говорит о том, виден ли конкретный тип вызывающему, а не об устройстве протокола.
Лечат ошибку компилятора о несовпадающих underlying types через AnyView повсюду. В SwiftUI это мешает сравнивать иерархию при обновлениях и бьёт по производительности, правильнее @ViewBuilder или Group.