2017-03-02 3 views
12

Pourquoi le code suivant génère-t-il une erreur?Pourquoi une exigence de propriété get-only dans un protocole ne peut-elle pas être satisfaite par une propriété conforme?

protocol ProtocolA { 
    var someProperty: ProtocolB { get } 
} 

protocol ProtocolB {} 
class ConformsToB: ProtocolB {} 

class SomeClass: ProtocolA { // Type 'SomeClass' does not conform to protocol 'ProtocolA' 
    var someProperty: ConformsToB 

    init(someProperty: ConformsToB) { 
     self.someProperty = someProperty 
    } 
} 

The answer in this similar question est logique. Cependant, dans mon exemple, la propriété est get-only. Pourquoi cela ne fonctionnerait-il pas? Est-ce une lacune de Swift, ou y a-t-il une raison à cela?

+0

Merci pour les liens. C'est malheureux, mais bon à savoir! – solidcell

+1

Si vous voulez ce comportement, dans 'ProtocolA', vous devriez avoir' associatedtype T: ProtocolB' et ensuite déclarer 'var someProperty: T {get}' – BallpointBen

+0

Cela fonctionnerait comme solution de contournement entre temps jusqu'à ce qu'il soit (espérons-le) corrigé, mais Je suis vraiment hésitant à ajouter un type associé, car cela ferait bouillir cette connaissance dans le reste du graphe d'objets, qui devient rapidement incontrôlable. – solidcell

Répondre

12

Il n'y a pas vraie raison pour laquelle cela ne devrait pas être possible, en lecture seule exigence de propriété peut être covariant, en retournant un ConformsToB exemple d'une propriété typée comme ProtocolB est parfaitement légal.

Swift ne le supporte pas actuellement. Pour ce faire, le compilateur doit générer a thunk entre la table des témoins du protocole et l'implémentation conforme afin d'effectuer la (les) conversion (s) de type nécessaire (s). Par exemple, une instance ConformsToB doit être encadréein an existential container afin d'être tapée comme ProtocolB (et l'appelant ne peut pas le faire car il ne sait rien de l'implémentation appelée).

Mais encore une fois, il n'y a aucune raison que le compilateur ne puisse pas faire cela. Il y a plusieurs rapports de bugs ouverts sur ce, this one qui est spécifique à la lecture seule des exigences de propriété et this general one, dans lequel Slava Pestov, membre de l'équipe Swift, dit:

[...] nous voulons les témoins de protocole et la méthode remplacent dans tous les cas où une conversion de fonction est autorisée

Cela ressemble vraiment à quelque chose que l'équipe Swift cherche à implémenter dans une future version du langage.

Dans le même temps cependant, comme @BallpointBen says, une solution consiste à utiliser un associatedtype:

protocol ProtocolA { 
    // allow the conforming type to satisfy this with a concrete type 
    // that conforms to ProtocolB. 
    associatedtype SomeProperty : ProtocolB 
    var someProperty: SomeProperty { get } 
} 

protocol ProtocolB {} 
class ConformsToB: ProtocolB {} 

class SomeClass: ProtocolA { 

    // implicitly satisfy the associatedtype with ConformsToB. 
    var someProperty: ConformsToB 

    init(someProperty: ConformsToB) { 
     self.someProperty = someProperty 
    } 
} 

Mais cela est tout à fait insatisfaisante, car cela signifie que ProtocolA n'est plus utilisable comme un type (parce qu'il a associatedtype exigences). Cela change aussi ce que dit le protocole. A l'origine, il a dit que someProperty pourrait retourner quoi que ce soit qui était conforme à ProtocolB - maintenant il est dit qu'une mise en œuvre de someProperty traite avec un seul type concret spécifique qui est conforme à ProtocolB.

Une autre solution est juste pour définir une propriété fictive afin de satisfaire à l'exigence de protocole:

protocol ProtocolA { 
    var someProperty: ProtocolB { get } 
} 

protocol ProtocolB {} 
class ConformsToB: ProtocolB {} 

class SomeClass: ProtocolA { 

    // dummy property to satisfy protocol conformance. 
    var someProperty: ProtocolB { 
     return actualSomeProperty 
    } 

    // the *actual* implementation of someProperty. 
    var actualSomeProperty: ConformsToB 

    init(someProperty: ConformsToB) { 
     self.actualSomeProperty = someProperty 
    } 
} 

Ici, nous écrivons essentiellement thunk pour le compilateur - mais il est pas particulièrement agréable car il ajoute une propriété inutile à l'API.

+1

Merci pour la réponse détaillée @ Hamish. J'ai déjà fait ce que vous avez suggéré (le wrapper de propriétés calculé), mais je suis d'accord, c'est dommage d'avoir à ajouter une autre propriété. – solidcell