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?
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.
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
}
}