In a concrete context, P.Type (where P is a protocol) actually means something different than in a generic context. In a generic context, T.Type is always the type of T.self. But in a concrete context, P.Type refers to the supertype of all types conforming to P, rather than the type of P.self. The type of P.self is called P.Protocol. Notably, it is not the case that P.self is P.Type. Also, if S: P it is not the case that S.self is P.Protocol:
protocol P {}
struct S: P {}
func f<T>(_ t: T.Type) {
print(T.Type.self)
}
print(P.Type.self) // P.Type
f(P.self) // P.Protocol
P.self is P.Type // false
S.self is P.Protocol // false
Thank you for a quick response. A follow up question would be, is it possible to capture the supertype of all types conforming to P in a generic context? That is, can we make the method
protocol P {}
struct S: P {}
struct TypeCaster<TypeCastingTo> {
init() {}
func cast<TypeCastingFrom>(_ _: TypeCastingFrom.Type) -> TypeCastingTo? {
return TypeCastingFrom.self as? TypeCastingTo
}
}
let caster = TypeCaster<P.Type>()
let firstResult = caster.cast(S.self) // works!
Your initial design requires you to pass an instance with static type P.Type in order to construct an instance of TypeCaster, but this version allows you (forces you) to pass the cast-to type directly as a generic argument.
Yep that would work too, anything where you are passing the type to cast to rather than an instance of the type to cast to should work. But there's no 'representative' instance of P.Type in the way there is for, like, S.Type (namely, S.self) since the only instantiations are types which conform to P, which may not even exist in the general case!
It is perhaps easier to express this now that we have SE-0335. When the generic type parameter T is bound to any P (an existential type including all values whose types conform to P), the type T.Type refers to (any P).Type (the type of the existential type any P), which is not the same type as any (P.Type) (an existential type including all types that conform to P).