Why doesn't this satisfy the type system?

import UIKit

protocol A {
    func f()
}

protocol B {
    var v: A? { get set }
}

class C: B {    //  Type 'C' does not conform to protocol 'B'
    var v: UIViewController? = nil
    init(v: UIViewController?) {
        self.v = v
    }
}

// Option A:
extension UIViewController: A {
    func f() {
        print("lol")
    }
}

// Option B:
extension A where Self: UIViewController {
    func f() {
        print("lol")
    }
}

So basically the compiler should have all the info it needs to assert that UIViewController indeed conforms to A and thus is safe to be put as the type of v in class C... But for some reason it just doesn't.
And I know using an associated type works here, but I really can't since it renders my protocol completely unusable for my use case (because of "can only be used as a generic constraint").

So is there anything I can do to fix this or do I have to resort to making v's type AnyObject and casting it like I would in Obj-C?

Thanks!

This isn’t safe, is it? If the code compiled, someone could type-erase C to B, set v to some A other than a UIViewController and then you would be in trouble once C would try to use v as a view controller.

2 Likes

Hi Tomáš,

Thanks for the response! Could you write a small example that would violate the type system (using type erasure) if my code would, hypothetically, compile? Because I'm not sure I quite get what you mean.

Thanks!

In code:

extension String: A {
    func f() {}
}

var c: B = C(v: UIViewController())
c.v = "boom"

This would be valid if your code compiled. And now if c tried to use v as a view controller, it would be in for a surprise. The key point is that anyone adopting B promises to work with any A, while C only works with As that are also view controllers.

1 Like