web-dev-qa-db-fra.com

Swift protocole avec clause "where Self"

En plus de cette syntaxe avec une extension de protocole:

protocol P {}
extension P where Self : UIView {}

... J'ai découvert par accident que vous pouvez utiliser la même clause where sur le protocole lui-même:

protocol P where Self : UIView {}

Notez que ceci est pas identique à une clause where contraignant un protocole générique, et ne pas lui-même fait de P un protocole générique.

Mes expériences semblent montrer que seul un deux-points peut être utilisé ici, et la chose après le deux-points doit être une classe ou un protocole (qui peut être générique).

Je suis devenu curieux: comment cela a-t-il échappé à mon attention? Je suis donc allé à la recherche de preuves de son apparition. En Swift 3.0, la première syntaxe est légale mais pas la dernière. En Swift 3.3, les deux sont légales. Donc la seconde la syntaxe doit avoir été introduite discrètement dans quelque chose comme Swift 3.2. Je dis "tranquillement" parce que je ne trouve rien à ce sujet dans les notes de version.

À quoi sert la deuxième syntaxe? Est-ce, comme il apparaît, juste un moyen pratique de s'assurer qu'aucun autre type ne peut adopter ce protocole? Les en-têtes Swift ne semblent pas en faire usage.

21
matt

La possibilité de mettre des contraintes de superclasse sur les déclarations de protocoles (c'est-à-dire de pouvoir définir protocol P where Self : CC est le type d'une classe) était une conséquence prématurée de
SE-0156 , et la syntaxe aurait dû être rejetée dans Swift 4.x jusqu'à ce que la fonctionnalité soit implémentée. Tentative d'utilisation de cette fonctionnalité dans = Swift 4.x peut provoquer une erreur de compilation et des plantages , donc j'éviterais de l'utiliser jusqu'à Swift 5.

Dans Swift 5 (Xcode 10.2) la fonctionnalité a maintenant implémentée . De les notes de version :

Les protocoles peuvent désormais contraindre leurs types conformes à ceux qui sous-classent une classe donnée. Deux formulaires équivalents sont pris en charge:

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

Swift 4.2 a accepté le deuxième formulaire, mais il n'était pas entièrement implémenté et pouvait parfois se bloquer au moment de la compilation ou de l'exécution. ( SR-5581 ) (38077232)

Cette syntaxe place une contrainte de superclasse sur MyView qui restreint les types conformes à ceux héritant de (ou étant) UIView. De plus, l'utilisation de MyView est sémantiquement équivalente à une classe existentielle (par exemple UIView & MyView) en ce que vous pouvez accéder à la fois aux membres de la classe et aux exigences du protocole sur la valeur.

Par exemple, en développant l'exemple des notes de version:

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)
21
Hamish