2

I have structs that are conforming to protocols, but are using derived protocols, instead of directly using its parent protocol:

protocol A { }
protocol B: A { }

protocol C {
    var property: A { get }
}

struct Foo: C {
    let property: A
}

struct Bar: C {
    let property: B
}

// Error: Type 'Bar' does not conform to protocol 'C'

Why doesn't Bar fulfill the conformance because property is a sub-protocol of A.

TruMan1
  • 33,665
  • 59
  • 184
  • 335

1 Answers1

2

B describes a type that conforms to some set of rules. It is not itself a type that conforms to those rules. B does not conform to B, let alone anything it requires additional conformance to. protocol B:A says "anything that conforms to B also must conform to A." However, B does not conform to B, and so does not conform to A.

This has been answered many times on SO, but to repeat the "why," it comes down most simply to this example:

protocol A { 
    init()
}

func f(type: A.Type) {
    let a = type.init()
}

f(type: A.self)

If A itself conforms to A, then this should be legal. But what init should f call? In the presence of init and static requirements, it's not possible for protocols to conform to themselves.

While the specific covariance you want in this case is possible in principle, Swift doesn't have the ability to distinguish between legal and illegal covariance, and disallows all of it. For example, consider the small variation from immutable to mutable:

protocol C {
    var property: A { get set }
}

In this case, it is definitely impossible for Bar to conform (it would have to provide a setter for property that accepted any A, even though the getter would return a subtype of A). Swift is not able to distinguish all the cases where it is possible (generally when everything is immutable and there are no init or static requirements) from the cases where it isn't. There's been some discussion of allowing these kinds of cases where it's technically possible, but the concern is that very small changes to the protocol could then break your entire type structure, sometimes for non-obvious reasons. Instead, at least for now, type requirements are generally invariant.


A typical way to address this overall issue is to just provide accessors for the types you want. For example:

protocol A { }
protocol B: A { }

protocol C {
    var aProperty: A { get }
}

struct Foo: C {
    let aProperty: A
}

struct Bar: C {
    var aProperty: A { return bProperty }
    let bProperty: B
}

This provides greater flexibility (you can make bProperty a var if you like), and makes your code more explicit.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thanks for the detailed answer. It seems like generics would be the next step, but `associatedtype` couldn't do it either. – TruMan1 Jan 07 '19 at 19:46
  • It's not clear what your situation is, but given the code you've given, the next step would be to just have `Bar` return an `A` from `property` as required. If these's some `B` subtype requirement, then just have another property that returns that subtype (and have `property` be a computed getter that returns it too). I've been doing it that way for years in ObjC, and it works just as well (and for the same reasons) in Swift, and also happens to make everything very clear. – Rob Napier Jan 07 '19 at 19:49
  • For example, I might have a type with `let ioError: IOError` and then have `var error: Error { return ioError }`. – Rob Napier Jan 07 '19 at 19:51
  • In my opinion you should add that “workaround” to your answer. – matt Jan 07 '19 at 20:31