У меня есть такой код:
protocol CoreTypes {
associatedtype TypeA
// ...more types and various operations between them
}
protocol Context<Types> {
associatedtype Types: CoreTypes
var valueA: Types.TypeA { get }
func setValueA(_ b: Types.TypeA)
}
class Operation<Types: CoreTypes> {
var context: any Context<Types>
init(context: some Context<Types>) {
self.context = context
}
func perform() {
context.setValueA(context.valueA)
}
}
Однако context.setValueA(context.valueA)
не компилируется. Ошибка:
Невозможно преобразовать значение типа «Любой» в ожидаемый тип аргумента «Types.TypeA».
Как будто Swift не понимает, что эти два определения используют один и тот же тип:
var valueA: Types.TypeA { get }
func setValueA(_ b: Types.TypeA)
Я знаю, что могу решить эту конкретную проблему, определив Context<TypeA>
, но мне нужно использовать его косвенно, т. е. из Types.TypeA
. Есть ли способ заставить это работать?
Это также отлично работает, если я конвертирую Context
в класс, т.е.:
class Context<Types: CoreTypes> {
var valueA: Types.TypeA { preconditionFailure() }
func setValueA(_ b: Types.TypeA) { preconditionFailure() }
}
Таким образом, я могу удалить any
из var context: Context<Types>
, но использование этих «фальшивых абстрактных классов» делает вещи менее безопасными во время компиляции...
Итак, есть ли лучший способ использовать AssociateType из другого типа?
Вы можете написать общую вспомогательную функцию, чтобы открыть экзистенциальный тип (SE-0352):
func perform() {
func helper<T: Context<Types>>(_ x: T) {
x.setValueA(x.valueA)
}
helper(context)
}
Тем не менее, насколько я понимаю, SE-0353, context.setValueA(context.valueA)
также должно быть возможным, поскольку связанный тип Context
ограничен конкретным типом.
context.setValueA(context.valueA)
компилируется, если бы это было:
var valueA: Types { get }
func setValueA(_ b: Types)
Так что вполне возможно, что команда Swift не учла этот случай при разработке/реализации SE-0353.
Другой альтернативой было передать оба типа протоколу Context
в качестве общих аргументов и ограничить их тем, что это одно и то же. Это не идеально (и не то, о чем я просил изначально), но в моем случае хорошо работает в определенных ситуациях:
protocol Context<Types, TypeA> where TypeA == Types.TypeA {
associatedtype Types: CoreTypes
var valueA: Types.TypeA { get }
func setValueA(_ b: Types.TypeA)
}
class Operation<Types: CoreTypes> {
var context: any Context<Types, Types.TypeA>
init(context: some Context<Types, Types.TypeA>) {
self.context = context
}
func perform() {
context.setValueA(context.valueA)
}
}
Спасибо! Удивительно, но это
helper
действительно работает! Это очень изящный трюк для преобразованияany
вsome
.