2 votes

Pourquoi un sous-protocole ne remplit-il pas la conformité pour son parent ?

J'ai des structures qui se conforment aux protocoles, mais qui utilisent des protocoles dérivés, au lieu d'utiliser directement leur protocole parent :

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'

Pourquoi est-ce que Bar remplir la conformité parce que property est un sous-protocole de A .

2voto

Rob Napier Points 92148

B décrit un type qui se conforme à un certain ensemble de règles. Il ne s'agit pas lui-même un type qui se conforme à ces règles. B n'est pas conforme à B sans parler de tout ce à quoi il faut se conformer. protocol B:A dit "tout ce qui est conforme à B doit également être conforme à A ." Cependant, B n'est pas conforme à B et n'est donc pas conforme à A .

Cette question a été traitée à de nombreuses reprises sur SO, mais pour répéter le "pourquoi", il suffit de prendre cet exemple :

protocol A { 
    init()
}

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

f(type: A.self)

Si A est lui-même conforme à A alors cela devrait être légal. Mais qu'est-ce que init devrait f appel ? En présence de init y static il n'est pas possible pour les protocoles de se conformer à eux-mêmes.

Bien que la covariance spécifique que vous souhaitez dans ce cas soit possible en principe, Swift n'a pas la capacité de distinguer la covariance légale de la covariance illégale, et la désapprouve entièrement. Par exemple, considérez la petite variation d'immuable à mutable :

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

Dans ce cas, il est définitivement impossible pour Bar pour se conformer (il devrait fournir un paramètre pour le paramètre property qui a accepté tout A même si le récupérateur renvoie un sous-type de A ). Swift n'est pas capable de distinguer tous les cas où cela est possible (généralement lorsque tout est immuable et qu'il n'y a pas de init ou static ) des cas où elle ne l'est pas. Il a été question d'autoriser ce genre de cas lorsque c'est techniquement possible, mais le souci est que de très petites modifications du protocole pourraient alors casser toute votre structure de type, parfois pour des raisons non évidentes. Au lieu de cela, du moins pour le moment, les exigences de type sont généralement invariantes.


Une façon typique de résoudre ce problème global est de fournir des accesseurs pour les types que vous voulez. Par exemple :

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
}

Cela offre une plus grande flexibilité (vous pouvez faire bProperty a var si vous voulez), et rend votre code plus explicite.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X