Honey Honey - 1 month ago 8
Swift Question

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?

Answer

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.

Comments