Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift protocol with "where Self" clause

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.

like image 359
matt Avatar asked Jun 18 '18 15:06

matt


1 Answers

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 in Swift 4.x until the feature was implemented. Attempting to use this feature in Swift 4.x can cause miscompilation and crashes, so I would avoid using it until Swift 5.

In Swift 5 (Xcode 10.2) the feature has now been implemented. From the release notes:

Protocols can now constrain their conforming types to those that subclass a given class. Two equivalent forms are supported:

protocol MyView: UIView { /*...*/ }
protocol MyView where Self: UIView { /*...*/ } 

Swift 4.2 accepted the second form, but it wasn’t fully implemented and could sometimes crash at compile time or runtime. (SR-5581) (38077232)

This syntax places a superclass constraint on MyView which restricts conforming types to those inheriting from (or being) UIView. In addition, the usage of MyView is semantically equivalent to a class existential (e.g UIView & MyView) in that you can access both members of the class and requirements of the protocol on the value.

For example, expanding upon the release notes' example:

protocol MyView : UIView {
  var foo: Int { get }
}

class C : MyView {} // error: 'P' requires that 'C' inherit from 'UIView'

class CustomView : UIView, MyView {
  var foo: Int = 0
}

// ...

let myView: MyView = CustomView(frame: .zero)

// We can access both `UIView` members on a `MyView` value
print(myView.backgroundColor as Any)

// ... and `MyView` members as usual.
print(myView.foo)
like image 149
Hamish Avatar answered Nov 20 '22 01:11

Hamish