protocol AnimalFeed {
init()
}
protocol Animal {
associatedtype Feed: AnimalFeed
func eat(_ food: Feed)
}
struct Hay: AnimalFeed {}
struct Carrot: AnimalFeed {}
struct Cow: Animal {
func eat(_ food: Hay) {
print("I eat hay")
}
}
struct Rabbit: Animal {
func eat(_ food: Carrot) {
print("I eat carrot")
}
}
struct Farm {
func feed<T: Animal>(_ animal: T) {
animal.eat(T.Feed())
}
func feedAll<T: Animal>(_ animals: [T]) {
animals.forEach { feed($0)}
}
}
let farm = Farm()
farm.feed(Rabbit())
farm.feedAll([Cow(), Rabbit()])
The last line results in an error: Type of expression is ambiguous without more context How do we work around this? I want to be able to pass an array of structs which conform to Animal protocol.
CodePudding user response:
This cannot be done in Swift 5.5
when you write
func feedAll<T: Animal>(_ animals: [T]) {
animals.forEach { feed($0)}
}
you're saying, "Farm can feed an array of any one Animal." The compiler looks for a single type conforming to Animal, Cow, and Rabbit, and can't find it. What you really want is, "Farm can feed an array of any objects conforming to Animal."
func feedAll(_ animals: [Animal]) {
animals.forEach { feed($0) }
}
But this angers Swift 5.5
Protocol 'Animal' can only be used as a generic constraint because it has Self or associated type requirements
Protocol 'Animal' as a type cannot conform to the protocol itself
You can work around this by constructing a "box" type that captures the inner type (often called type erasure) and explicitly wrap each animal in your array in a box. For example
struct AnimalFeeder {
let feed: () -> Void
init<T: Animal>(_ animal: T) {
feed = {
animal.eat(T.Feed())
}
}
}
func feedAll(_ animals: [AnimalFeeder]) {
animals.forEach { $0.feed() }
}
farm.feedAll([AnimalFeeder(Cow()), AnimalFeeder(Rabbit())])