In addition to this syntax with a protocol extension:
protocol P {}
extension P where Self : UIView {}
... I discovered by accident that you can use the same where clause on the protocol itself:
protocol P where Self : UIView {}
Notice that this is not the same as a where clause constraining a generic protocol, and does not itself make P a generic protocol.
My experiments seem to show that only a colon can be used here, and the thing after the colon must be a class or a protocol (which may be generic).
I became curious: how did this escape my notice? So I went hunting for evidence of when it arose. In Swift 3.0, the former syntax is legal but not the latter. In Swift 3.3, both are legal. So the latter syntax must have been quietly introduced in something like Swift 3.2. I say "quietly" because I can't find anything about it in the release notes.
What is the second syntax for? Is it, as it appears, just a convenient way of making sure no other type can adopt this protocol? The Swift headers do not seem to make any use of it.
The ability to put superclass constraints on protocols declarations (that is, being able to define protocol P where Self : C
where C
is the type of a class) was a premature consequence of
SE-0156, and the syntax should have been rejected until the feature was implemented. It currently (in Swift 4.x) has a lot of sharp edges around it, so I would steer clear of it for now – see my answer here for more info.
The feature itself has only been recently implemented for Swift 5 (you can try it out for yourself in a master snapshot), allowing you to use the syntax of either:
protocol P where Self : C {}
or, more consicely:
protocol P : C {}
in order to place a superclass constraint on P
that restricts conforming types to being those that inherit from (or are) C
. In addition, the usage of P
is semantically equivalent to a class existential (e.g C & P
) in that you can access both members of the class and requirements of the protocol on the value.
For example, using your UIView
example:
protocol P : UIView { // okay.
var foo: Int { get }
}
class C : P {} // error: 'P' requires that 'C' inherit from 'UIView'
class MyView : UIView, P { // okay.
var foo: Int = 0
}
// ...
let p: P = MyView(frame: .zero)
print(p.backgroundColor as Any) // we can access both `UIView` members on a `P` value
print(p.foo) // ... and `P` members as usual.