0

Suppose I have a generic class-bound protocol called Car that has an associated type which inherits from AnyObject. This protocol has a variable and an addPassenger() function defined and provides a default implementation for it.

I need the associated type to inherit from AnyObject because I want to be able to use === to filter out instances I already know about when calling addPassenger().

protocol Car: class {
    associatedtype T: AnyObject
    var passengers: [T] { get set }
    func addPassenger(_ passenger: T)
}

extension Car {
    func addPassenger(_ passenger: T) {
        guard passengers.index(where: { $0 === passenger }) == nil else {
            return
        }
        passengers.append(passenger)
    }
}

I then have another protocol called Passenger which must be usable with Car:

protocol Passenger: AnyObject {}

Finally, suppose I have an instance of a class called SomeClass which conforms to the Car protocol and defines an array of Passengers:

class SomeClass: Car {
    internal var passengers: [Passenger] = []
}

However, this is not possible because apparently Passenger does not conform to AnyObject:

 error: type 'SomeClass' does not conform to protocol 'Car'
 note: unable to infer associated type 'T' for protocol 'Car'
 note: inferred type 'Passenger' (by matching requirement 'passengers') is invalid: does not conform to 'AnyObject'

Any idea what am I missing?

clawoo
  • 791
  • 6
  • 14
  • 1
    I was dealing with similar issues and asked this question : http://stackoverflow.com/questions/36867827/how-to-make-protocol-associated-type-require-protocol-inheritance-and-not-protoc. I think that currently the associated type must be a concrete type and not a protocol one which inherits from base protocol requirements – Zell B. Apr 25 '17 at 14:41
  • 1
    Compare [Unable to use protocol as associatedtype in another protocol in Swift](http://stackoverflow.com/q/37360114/2976878) & [Protocol doesn't conform to itself?](http://stackoverflow.com/a/43408193/2976878) – `Passenger` is indeed not a type that conforms to `AnyObject`. – Hamish Apr 25 '17 at 14:50

1 Answers1

1

A concrete type must be used as the associatedType when implementing the protocol. I think this has to do with the fact that the type used for generic type needs to be known at compile time. So, your case doesn't compile for the same reason as this:

protocol A { }

class B: A { }

func f<T: A>(x: T) { }

// doesn't compile
let x: A = B()
f(x: x)

// compiles
let x = B()
f(x: x)

Because the generic type T needs to be known at compile time.

In your case, if you want SomeClass to work with different types that conform to Passenger you can use generics to make it work:

class SomeClass<T: Passenger>: Car {
    var passengers: [T] = []
}

or, you could of course use a base class instead

class BasePassenger: AnyObject {}

class SomeClass: Car {
    var passengers: [BasePassenger] = []
}

Hope this helps! Good luck!

timaktimak
  • 1,380
  • 1
  • 12
  • 21
  • "A concrete type must be used as the associatedType when implementing the protocol." - that's interesting, is this in the documentation? I can't remember seeing it. – clawoo Apr 26 '17 at 12:57