Your question contains the false premise that
func doMoreCoolThings(awesome: Amazing) -> Amazing { ... }
is equivalent to:
func doMoreCoolThings<T: Amazing>(awesome: T) -> T { ... }
This is not at all true. Say I have these conforming types to work with:
struct AmazingApple: Amazing {
func doAmazingStuff() { print("I'm an apple who talks!") }
}
struct AmazingBanana: Amazing {
func doAmazingStuff() { print("I'm a banana who talks!") }
}
Look at what the first piece of code let's me do:
func doMoreCoolThings(awesome: Amazing) -> Amazing {
return AmazingBanana()
}
let input = AmazingApple()
let result = doMoreCoolThings(awesome: input)
print("The input is of type \(type(of: input))")
print("The return is of type: \(type(of: result))")
The parameter type and return type are different, even though they're both subtypes of Amazing
.
On the other hand, looks what happens when you try to do the same with the generic variant:
Generics are used to express relationships between types.
The non-generic code expresses:
"Let doMoreCoolThings(awesome:)
be a function that takes some value whose type is a subtype of Awesome
, and returns a value whose type is a subtype of Awesome
."
The generic code expresses:
"Let doMoreCoolThings(awesome:)
be a function that takes some value whose type is a subtype of some type, call it T
, and returns a value those type is a subtype of T
, where T
itself is a subtype of Amazing
."
Notice that the second quote expresses the requirement for the parameter and the return to be of the same type. It's not sufficient to just both have them be subtypes of Amazing
.