I have this sort of code:
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)
}
}
However, context.setValueA(context.valueA)
doesn't compile. The error is:
Cannot convert value of type 'Any' to expected argument type 'Types.TypeA'
It's like Swift doesn't understand that these two definitions use the same type:
var valueA: Types.TypeA { get }
func setValueA(_ b: Types.TypeA)
I know that I could fix this specific issue by defining Context<TypeA>
, but I need to use it indirectly, i.e. from Types.TypeA
. Is there a way to make this work?
It also works fine if I convert Context
to a class, i.e.:
class Context<Types: CoreTypes> {
var valueA: Types.TypeA { preconditionFailure() }
func setValueA(_ b: Types.TypeA) { preconditionFailure() }
}
as that way I can drop any
from var context: Context<Types>
, but using these "fake abstract classes" makes things less compile time safe...
So is there a better way to use associatedtype from another type?
You can write generic helper function to open up the existential type (SE-0352):
func perform() {
func helper<T: Context<Types>>(_ x: T) {
x.setValueA(x.valueA)
}
helper(context)
}
That said, from my understanding of SE-0353, context.setValueA(context.valueA)
should also be possible, since the associated type of Context
is constrained to a concrete type.
context.setValueA(context.valueA)
compiles if it were:
var valueA: Types { get }
func setValueA(_ b: Types)
So it is possible that the Swift team didn't think of this case when designing/implementing SE-0353.