-1

Let's say I have a protocol:

protocol Amazing {
     func doAmazingStuff()
}

And then I have a function that takes that type to do things with it:

func doMoreCoolThings<T: Amazing>(awesome: T) -> T { ... }

What's the difference between that and just doing something like this?

func doMoreCoolThings(awesome: Amazing) -> Amazing { ... }

My question here is why do we even bother using generics when we can just put in the type like that?

UPDATE So I can see the point in using genetics instead of the:

func doMoreCoolThings(awesome: Amazing) -> Amazing { ... }

However is there really any use to using that function or would it always be better to use generics?

Harish
  • 1,374
  • 17
  • 39
  • 1
    https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html – adev Jul 30 '17 at 05:33

2 Answers2

3

This is just one benefit that I can immediately think of. I'm sure there are lots more.

Let's say We have two classes A and B that both conform to Amazing.

If we pass A() into this function:

func doMoreCoolThings<T: Amazing>(awesome: T) -> T { ... }

like this:

let val = doMoreCoolThings(awesome: A())

We are sure that val is of type A and the compiler knows that too. This means we can access A's members using val.

On the other hand if we pass A() into this function:

func doMoreCoolThings(awesome: Amazing) -> Amazing { ... }

like this:

let val = doMoreCoolThings(awesome: A())

val's compile time type is Amazing. The compiler does not know what type of Amazing it is. Is it A or is it B or is it something else? The compiler doesn't know. You will have to cast the result to A in order to access its members.

let a = val as! A

The cast is also not guaranteed to succeed.

If you put these casts everywhere your code will soon become very messy.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Yeah that makes sense, could you checkout my update though? – Harish Jul 30 '17 at 06:27
  • @Harish Regarding your update, my opinion is that you should use interfaces when the return type isn't necessarily the same as the parameter passed in, or in cases where you don't expect the client to cast the result. Otherwise use generics. – Sweeper Jul 30 '17 at 06:29
  • But if the return type is different from the parameter passed in can’t you do this: ‘’’func doMoreCoolThings(awesome: T) -> R’’’ – Harish Jul 30 '17 at 06:33
  • 1
    @Harish Yes you can do that. What I mean is that when the return type is decided by the method's implementation, not by the caller. – Sweeper Jul 30 '17 at 06:34
1

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.

Alexander
  • 59,041
  • 12
  • 98
  • 151