Home > database >  Using array of swift generics in method with swift5
Using array of swift generics in method with swift5

Time:06-11

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())])
  • Related