clawoo
clawoo

Reputation: 791

Generic protocol with an associated type inheriting from AnyObject does not see another protocol as inheriting from AnyObject

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?

Upvotes: 0

Views: 691

Answers (1)

timaktimak
timaktimak

Reputation: 1380

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!

Upvotes: 1

Related Questions