Обязаны ли две функции, возвращающие some-тип одного протокола, возвращать один и тот же скрытый конкретный тип?
Нет. Каждое объявление с some P создаёт собственный непрозрачный тип, даже если функции фактически возвращают один и тот же конкретный тип. Совпадение протокола не устанавливает равенство скрытых типов между функциями.
Opaque result types появились как компромисс между двумя потребностями: скрыть детали реализации API и сохранить статическую информацию о конкретном типе для компилятора. В отличие от existential-значения any P, результат some P не стирает тип полностью: вызывающий код знает, что внутри находится один конкретный тип, но не знает какой именно.
Такой подход позволяет библиотеке менять внутреннюю реализацию без публикации конкретного типа, сохраняя при этом преимущества статической типизации и generic-оптимизаций.
Ошибочно считать some P синонимом any P или обозначением «любой тип, соответствующий P». Если две функции возвращают some P, их результаты нельзя автоматически считать взаимозаменяемыми значениями одного типа.
Это особенно важно для generic-функций, которым требуется, чтобы несколько аргументов имели один и тот же generic-тип. Простого совпадения протокола недостаточно, поэтому попытка передать результаты двух разных opaque-функций в такой контекст может завершиться ошибкой компиляции.
У каждой функции свой скрытый тип результата. Он фиксирован внутри конкретного объявления функции, но не раскрывается вызывающему коду. Все ветви одной функции, возвращающей some P, должны возвращать один и тот же конкретный тип.
Между разными функциями такой связи нет. Даже если обе функции возвращают Text, компилятор рассматривает их результаты как два разных opaque-типа:
Здесь T должен быть одним и тем же типом для обоих аргументов. first() и second() действительно возвращают значения одного конкретного типа Text на уровне реализации, но это равенство не является частью публичного контракта и не выводится между независимыми opaque-результатами.
Если нужна работа с любыми двумя значениями протокола независимо от их конкретных типов, применяют existential any Renderable, если требования протокола это позволяют. Если же нужно гарантировать одинаковый конкретный тип, связь следует выразить через общий generic-параметр или другой явно заданный type-level контракт.
Главный компромисс таков: some P сохраняет больше статической информации и обычно лучше подходит для результата конкретной функции, но не предоставляет вызывающему коду имени скрытого типа и не создаёт автоматических связей с результатами других функций.
Команда проектирует фабрики UI-компонентов. Две фабрики возвращают some View, а затем разработчик пытается передать оба результата в функцию, которая принимает два значения одного generic-типа.
Рассматривались варианты:
any View — упрощает совместное хранение и передачу, но стирает конкретный тип и может ограничить доступ к требованиям с Self или associatedtype;some View — хорошо скрывает реализацию, но не гарантирует совпадение типов результатов разных фабрик.Выбранное решение зависит от контракта. Если фабрики должны возвращать один и тот же тип, это оформляют общей generic-абстракцией или одной фабрикой с несколькими ветвями, возвращающими единый opaque-тип. Если типы могут различаться, используют type erasure или API, принимающий разные generic-параметры.
Гарантирует ли some P один и тот же тип при каждом вызове функции?
Да. Тип фиксирован объявлением функции, а не выбирается заново при каждом вызове. Разные вызовы одной функции возвращают значения одного и того же скрытого типа.
Может ли одна функция вернуть значения разных конкретных типов в разных ветвях при результате some P?
Нет. Все ветви должны согласоваться с одним конкретным типом результата. То, что несколько типов соответствуют одному протоколу, не делает их взаимозаменяемыми для opaque-результата.
Чем принципиально отличается some P от any P?
some P скрывает конкретный тип, но сохраняет его единственность и статическую идентичность внутри контракта конкретного объявления. any P хранит значение произвольного типа, соответствующего протоколу, и стирает его конкретный тип за existential-обёрткой. Поэтому some P лучше подходит для сохранения generic-связей, а any P — для хранения и передачи значений разных конкретных типов через общий протокол.