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.