Watching: Embrace Swift generics - WWDC22 - Videos - Apple Developer

protocol Animal {
    func eat()
}

struct Cow: Animal {
    func eat() {
        print("eats grass")
    }
}

struct Cat: Animal {
    func eat() {
        print("eats meat")
    }
}

func feed <T>(animal: T) where T: Animal {
    animal.eat()
}

In the talk, it is said that the function an be simplified to

func simplifiedFeed (animal: some Animal) {
    animal.eat()
}

But what if the generic parameter was for a returning type? How can I write the following using some?

func feedB <T>(animal: T) -> T where T: Animal {
    animal.eat()
    return animal
}

Neither of the following functions offer protection (throw errors) to ensure the parameter type and returning types are the same.

func feedSomeB (animal: some Animal) -> Animal {
    animal.eat()
    return Cat() // NO ERROR - BAD
}

func feedSomeC (animal: some Animal) -> some Animal {
    animal.eat()
    return Cat() // NO ERROR - BAD
}

feedSomeB(animal: Cow())
feedSome(animal: Cow())

Every use of some introduces a new independent generic parameter, so it's not the best tool for communicating cases where there is a relationship required between two parameters or returns. This is one time when it's better to use explicitly named generic parameters.

6 Likes

That makes sense. Like I can't even do the same for

func feedSomeC (animal: some Animal, another: some Animal)

Any other limitation of some?

You cannot do

func getAnimal(dog: Bool) -> some Animal {
  if dog { Dog() } else { Cat() }
}

with struct Dog: Animal { … }.

In other words, all branches need to return the same actual type.