4

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?

Community
  • 1
  • 1
mfaani
  • 33,269
  • 19
  • 164
  • 293
  • 1
    Also be sure to see [this Medium post](https://buildingvts.com/advanced-swift-protocols-d683cae12caf) it offers **great** insight about the issue. – mfaani May 27 '17 at 10:54

2 Answers2

7

You just have to return Self

edit/update:

Note: You can extend all numeric types (Integer & FloatingPoint) in Swift 4 extending the Numeric Protocol

Swift 4

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

Swift 3

extension Integer {
    func squared() -> Self { 
        return self * self
    }
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • Leo, can you take a look at this [comment](https://stackoverflow.com/questions/39338692/why-do-i-get-the-error-protocol-can-only-be-used-as-a-generic-constraint-beca#comment87254598_39351340) on the accepted answer? I'm thinking that answer is somewhat wrong and yours is correct. Am I right? Can you elaborate a bit? Also other than the new `Numeric` protocol, is there anything new in Swift4 which would affect this question? – mfaani May 01 '18 at 14:57
  • @Honey there is nothing to elaborate. The accepted answer explains why did you get the error, which is what you asked. I just provided the solution to your question. Note that it is up to you to decide which one to mark as correct. – Leo Dabus May 01 '18 at 15:05
3

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.

kandelvijaya
  • 1,545
  • 12
  • 20
  • What's with the underscore? – mfaani Sep 07 '16 at 20:41
  • 1
    They are part of swift internal implementation protocols. Although you can access them publicly, i would not do that. – kandelvijaya Sep 08 '16 at 05:45
  • I means what's the difference between `_IntegerType` & `IntegerType`? – mfaani Sep 08 '16 at 18:41
  • Any protocol that follows _ signifies that this protocol should not be used publicly. This is part of the Swift Standard Library implementation. Time and again they will revise this internal protocol in a way it does adhere to the high level view. And if you do use this publicly although Xcode will not suggest, when the internals change you might need to redo. – kandelvijaya Sep 09 '16 at 12:48
  • It's been more than year. And I've slowly I've started to use generics for my code and have a better understanding. 2 Questions: 1. _is not sufficient information for the compiler to deduce at compile time._ I don't get that. I mean the type of a generic parameter is inherently unknown in all its functions. Why can't it be the same for the extension you wrote. Why is that any different? 2. Additionally you said: _A function return type can only be a concrete Type._ But many functions return `T`. Doesn't that contradict what you just said? – mfaani May 01 '18 at 14:51
  • @Honey: 1. It is not true that the type of a generic parameter is unknown. The compiler resolves the type of generic parameters based on what type its callers specify. 2. It's true that some protocols declare funcs that return `T`, but those protocols are always adopted by types that define `T`. They do this either concretely, by saying that `T` is some concrete type, or by declaring their own generic parameter, putting the responsibility of specifying the concrete type onto _their_ caller. Either way, `T` gets associated to a concrete type. If it didn't, the compiler would prohibit it. – erikprice Feb 22 '19 at 13:48