What's the difference between these two methods of declaring a generics superclass with or without the 'where' clause?
func foo<T: SomeClass>(object: T) -> Array<T>
func foo<T>(object: T) -> Array<T> where T: SomeClass
What's the difference between these two methods of declaring a generics superclass with or without the 'where' clause?
func foo<T: SomeClass>(object: T) -> Array<T>
func foo<T>(object: T) -> Array<T> where T: SomeClass
This is clearly stated in the Swift guide:
The requirements in a generic where clause specify that a type parameter inherits from a class or conforms to a protocol or protocol composition. Although the generic where clause provides syntactic sugar for expressing simple constraints on type parameters (for instance,
<T: Comparable>
is equivalent to<T> where T: Comparable
and so on), you can use it to provide more complex constraints on type parameters and their associated types. For instance, you can constrain the associated types of type parameters to conform to protocols. For example,<S: Sequence> where S.Iterator.Element: Equatable
specifies thatS
conforms to theSequence
protocol and that the associated typeS.Iterator.Element
conforms to theEquatable
protocol. This constraint ensures that each element of the sequence is equatable.
Simply put, where
lets you specify constraints about the associated types of a generic parameter, while in <>
you can't do this.
Questions like these always require some practical examples, even the simplest to get basic idea. Otherwise it often remains theoretical.
As referenced by Sweeper, that which is after where
is called type constraint. Typically you specify one or more protocols which introduce conditions which must be met by passed object or a type. And as mentioned, it's only after where
where you can specify associatedtype
constraint of the generic type.
Consider the following example:
protocol MyProtocol {
associatedtype AType
func foo()
}
class MyClassInt : NSObject, MyProtocol {
typealias AType = Int
func foo() {
print(type(of: self))
}
}
class MyClassString : NSObject, MyProtocol {
typealias AType = String
func foo() {
print("I'm not implemented")
}
}
extension MyProtocol where Self.AType == Int {
func test() {
self.foo()
}
}
Now check our things:
let str = MyClassString()
str.test() // Won't compile !!
'MyClassString.AType' (aka 'String') is not convertible to 'Int'
This one compiles and runs:
let int = MyClassInt()
int.test()
In this example we have a protocol with the associatedtype
specified. And there's also an extension
which is only applied to our MyProtocol
with particular associatedtype
, in our case Int
. That extension defines single method. As you can see, compiler won't let us to call that method with the associatedtype
other than that specified by a constraint, even though MyClassString
also implements that protocol.