Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to say "same class" in a Swift generic

Tags:

generics

swift

If a Swift generic type constraint is a protocol name, I can require that two types, constrained to that protocol, be the same type. For example:

protocol Flier {}
struct Bird : Flier {}
struct Insect: Flier {}
func flockTwoTogether<T:Flier>(f1:T, f2:T) {}

The function flockTwoTogether can be called with a Bird and a Bird or with an Insect and an Insect, but not with a Bird and an Insect. That is the limitation I want. So far, so good.

However, if I try the same thing with a class name, it doesn't work:

class Dog {}
class NoisyDog : Dog {}
class WellBehavedDog: Dog {}
func walkTwoTogether<T:Dog>(d1:T, d2:T) {}

The problem is that I can call walkTwoTogether with a WellBehavedDog and a NoisyDog. This is what I want to prevent.

There are really two questions here:

  • Is there a way to say that walkTwoTogether can't be called with a WellBehavedDog and a NoisyDog?

  • Is this a bug? I ask because if I can't use a generic to say this, it is hard to see why it is useful for a generic constraint to be a class name at all, since we could get the same result just with a normal function.

like image 393
matt Avatar asked Sep 30 '22 07:09

matt


1 Answers

Not an answer, per se, but some more data perhaps... The problem is when you call:

walkTwoTogether(NoisyDog(), WellBehavedDog())

Swift can just treat both instances as if they're instances of Dog (aka, upcast) — we need that so we can call methods meant for class A with subclasses of A. (I know you know this.)

Swift doesn't upcast to protocols, so the only way to do it is to specify a protocol for the subclasses that the superclass doesn't conform to:

protocol Walkable {}
extension NoisyDog : Walkable {}
extension WellBehavedDog: Walkable {}
func walkTwoTogether<T: Dog where T: Walkable>(d1:T, d2:T) { }

walkTwoTogether(NoisyDog(), WellBehavedDog())
// error: type 'Dog' does not conform to protocol 'Walkable'

The error message explicitly shows what is going on — the only way to call this version of walkToTogether is to upcast the subclass instances to Dog, but Dog doesn't conform to Walkable.

like image 92
Nate Cook Avatar answered Nov 15 '22 09:11

Nate Cook