Search code examples
swiftgenericsprotocolsselfprotocol-extension

Why do I get the error “Protocol … can only be used as a generic constraint because it has Self or associated type requirements”?


I wrote an extension onto Int as below.

extension Int {
    func squared () -> Int {
        return self * self
    }
}

print(10.squared()) // works

The above code works. Now I want to extend the IntegerType protocol so that Int, UInt, Int64, etc would all conform. My code is as below.

extension IntegerType {

    func squared () -> IntegerType { // this line creates error

        return self * self

    }
}

I get error:

Protocol 'IntegerType' can only be used as a generic constraint because it has Self or associated type requirements

I already saw this question and its video & this question, still couldn't understand. I only understood that there is some associatedType which in this case is Self but couldn't connect the dots. I feel like also my lack of knowledge on the Generics subject is also a reason...

Can someone elaborate a bit on the subject and why does the extension create an error?


Solution

  • A function return type can only be a concrete Type.

    The point is Type. Anything struct, class or Protocols that are completely defined in themselves are pure Type. However when a protocol or struct depend on another Generic Type Placeholder such as T, then this is a partial type.

    Type are a data construct that compiler has to allocate certain memory.

    So something like this:

    let a = Array<T>() or let b = T is not sufficient information for the compiler to deduce at compile time.

    Hence, this wont work.

      extension IntegerType {
    
        func squared () -> IntegerType { // this line creates error
    
            return self * self
    
        }
    }
    

    Here, IntegerType is a partial type. It is a generic protocol that only when conformed can then we know the exact type. Similar to Array. Array itself is not a type. Its a generic container. Only when someone creates it with Array() or Array()... then it has a type.

    The same happened with you.

    public protocol IntegerType : _IntegerType, RandomAccessIndexType {
    

    then again,

    public protocol RandomAccessIndexType : BidirectionalIndexType, Strideable, _RandomAccessAmbiguity {
    @warn_unused_result
        public func advancedBy(n: Self.Distance) -> Self
    

    then again,

       public protocol _RandomAccessAmbiguity {
        associatedtype Distance : _SignedIntegerType = Int
       }
    

    Hence, as RandomAccessIndexType has Self requirement meaning until and unless someone conforms to it, Self is unknown placeholder. It is partial Type.

    Since IntegerType conforms to the RandomAccessIndexType and _RandomAccessAmbuiguity which requires Distance associated type too.

    Hence you cant do this too

    let a: IntegerType = 12
    

    Again IntegerType needs to know Self and Distance (associatedType).

    Int however provides the details like so

    public struct Int : SignedIntegerType, Comparable, Equatable {
        /// A type that can represent the number of steps between pairs of
        /// values.
        public typealias Distance = Int
    

    Hence you can do such

    let a:Int = 10
    

    because it provides Self for SignedIntegerType and Distance for its other counterpart.

    Simply put it:

    A partial type cannot be used where a concrete type can be. A partial type are good for other generics and constraining them.