Search code examples
swiftdebuggingreflectiondynamictypemetatype

Swift - what's the difference between metatype .Type and .self?


What's the difference between metatype .Type and .self in Swift?

Do .self and .Type return a struct?

I understand that .self can be used to check with dynamicType. How do you use .Type?


Solution

  • Here is a quick example:

    func printType<T>(of type: T.Type) {
        // or you could do "\(T.self)" directly and
        // replace `type` parameter with an underscore
        print("\(type)") 
    } 
    
    printType(of: Int.self) // this should print Swift.Int
    
    
    func printInstanceDescription<T>(of instance: T) {
        print("\(instance)")
    } 
    
    printInstanceDescription(of: 42) // this should print 42
    

    Let's say that each entity is represented by two things:

    • Type: # entitiy name #

    • Metatype: # entity name # .Type

    A metatype type refers to the type of any type, including class types, structure types, enumeration types, and protocol types.

    Source.

    You can quickly notice that this is recursive and there can by types like (((T.Type).Type).Type) and so on.

    .Type returns an instance of a metatype.

    There are two ways we can get an instance of a metatype:

    • Call .self on a concrete type like Int.self which will create a static metatype instance Int.Type.

    • Get the dynamic metatype instance from any instance through type(of: someInstance).

    Dangerous area:

    struct S {}
    protocol P {}
    
    print("\(type(of: S.self))")      // S.Type
    print("\(type(of: S.Type.self))") // S.Type.Type
    print("\(type(of: P.self))")      // P.Protocol
    print("\(type(of: P.Type.self))") // P.Type.Protocol
    

    .Protocol is yet another metatype which only exisits in context of protocols. That said, there is no way how we can express that we want only P.Type. This prevents all generic algorithms to work with protocol metatypes and can lead to runtime crashes.

    For more curious people:

    The type(of:) function is actually handled by the compiler because of the inconsistency .Protocol creates.

    // This implementation is never used, since calls to `Swift.type(of:)` are
    // resolved as a special case by the type checker.
    public func type<T, Metatype>(of value: T) -> Metatype { ... }