0

I simplify my trouble into a small demo about using Generic Type and protocol.here is the code.

protocol Food {

}

class Meat: Food {

}

class Cake: Food {

}

protocol EatProtocol {
    func eat<T: Food>(_: T)
}

class Person: EatProtocol {
    func eat<T>(_: T) where T : Food {
        print("eat food")
    }
}

class Man: Person {
    override func eat<T>(_: T) where T : Meat {
        print("eat meat")
    }
}

class Woman: Person {
    override func eat<T>(_: T) where T : Cake {
        print("eat cake")
    }
}

let man = Man()
let woman = Woman()
let manyPeople: [EatProtocol] = [man, woman]

let meat = Meat()
let cake = Cake()
let manyFood: [Food] = [meat, cake]

for (index, people) in manyPeople.enumerated() {
    let food = manyFood[index]
    people.eat(food)//error: Cannot invoke 'eat' with an argument list of type '(Food)'
}

the problem is I am sure that in for-loop item get right Food, but the compiler give me that error

huangxinyu
  • 247
  • 3
  • 11
  • 1
    I suspect this to be an XY problem. This doesn't seem to be a use case for using generics at all (polymorphism probably make more sense), but I would have to understand the real problem you're trying to solve before I could make that determination – Alexander May 24 '17 at 03:14
  • Why would array manyPeople: [EatProtocol] be of type EatProtocol and not type Person? Why can't men and womeb both eat cake and meat? – Brian Ogden May 24 '17 at 03:22
  • @BrianOgden manyPeople array type [EatProtocol] is same as [Person]. In this problem, one kind of person just can eat one food, this is why the error occur. – huangxinyu May 24 '17 at 03:26
  • Related: [Protocol doesn't conform to itself?](https://stackoverflow.com/a/43408193/2976878) – Hamish May 24 '17 at 06:02

1 Answers1

1

There's a fundamental issue here: Not all eaters can eat all kinds of food. The best I can think of in this particular example is to use a switch to enumerate the possible combinations, cast safely, and make the call:

protocol Food {}
class Meat: Food {}
class Cake: Food {}

protocol Eater {
    func eat<T: Food>(_: T)
}

class Person: Eater {
    func eat<T>(_: T) where T: Food {
        print("eat food")
    }
}

class Man: Person {
    override func eat<T>(_: T) where T: Meat {
        print("eat meat")
    }
}

class Woman: Person {
    override func eat<T>(_: T) where T : Cake {
        print("eat cake")
    }
}

let eaters: [Eater] = [Man(), Woman()]
let foods: [Food] = [Cake(), Cake()]

for (food, eater) in zip(foods, eaters) {
    switch (food, eater) {
        case let (meat as Meat, man as Man): man.eat(meat)
        case let (cake as Cake, woman as Woman): woman.eat(cake)
        //...
        default:
            print("\(eater) (of type: \(type(of: eater))) cannot eat \(food) (of type: \(type(of: food)))")
            continue
    }
}
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • 1
    Actually there's an even scarier issue here – the fact that Swift permits the overriding of `eat(_: T) where T: Food` with `func eat(_: T) where T: Meat`. Functions are contravariant with respect to their parameter types, so [permitting the override is nonsense/downright dangerous](https://gist.github.com/hamishknight/9f40055541dced330420b83ef04eb7e7). I'll file a bug when I get a moment. – Hamish May 24 '17 at 05:54
  • @Hamish,yes, you're right, in this situation, I have to guarantee someone will get the right food, something like you always give proper subscript to an array. – huangxinyu May 24 '17 at 06:21
  • @Hamish When I was looking at this late last night, I was thinking to myself that it didn't make sense. I'm glad I'm not going loony :p – Alexander May 24 '17 at 16:41