在回答问题之前,我将稍微重命名类型以使问题更清晰:
protocol P {
typealias ElementType
func foo(c: C<ElementType>) -> ElementType
func foo2() -> ElementType
}
class C<T> {
var p: P
init (p: P) {
self.p = p
}
func bar() -> T {
return p.foo(self)
}
}
在这种情况下,您会收到三个编译器错误:
error: <EXPR>:8:12: error: protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements
var p: P
^
<EXPR>:9:14: error: protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements
init (p: P) {
^
<EXPR>:13:16: error: 'P' does not have a member named 'foo'
return p.foo(self)
^ ~~~
有趣的是第一个/第二个(他们指出了同样的问题):“协议‘P’只能用作通用约束,因为它具有 Self 或关联类型要求”。
所以问题出在关联类型上。在当前配置中,您指定初始值设定项的参数和变量的类型为 P。但由于您为 P 指定了关联类型,因此该类型不够具体,无法用作正确的类型。仅实际指定内容的子类型ElementType
是可以使用的。但是,您可以指定一个必须是 P 子类型的通用参数。对于初始化程序,您可以编写
init <S:P>(p: S) {
self.p = p
}
这将消除初始化程序的编译器错误。现在编译器知道参数必须是 P 的子类型,并且有效的子类型始终指定 ElementType 是什么,所以它很高兴。
但这对你来说这行没有帮助:
var p: P
您仍然无法使用不完整类型P
这里。你可能想使用S
,但目前之间没有任何联系S
在初始化程序中,您将使用 S 作为变量的类型,但它们显然需要相同。
是时候向您的类引入第二个通用参数了:
class C<T, S:P> {
var p: S
init (p: S) {
self.p = p
}
func bar() -> T {
return p.foo(self)
}
}
几乎完成了,现在您已经正确指定了可用于变量的类型。但你的协议规范不正确:
func foo(c: C<ElementType>) -> ElementType
C 现在需要两个参数,您需要在此处指定它们。我们想在这里使用`C,但我们不能:
错误::3:17:错误
: type 'P' does not conform to protocol 'P'
func foo(c: C<ElementType, P>) -> ElementType
^
<EXPR>:2:15: note: associated type 'ElementType' prevents protocol from conforming to itself
typealias ElementType
Since P
未指定关联类型ElementType
它不正确地符合P
并且不能用在符合类型的地方P
是需要的。但有一个很好的特殊类型:Self
。它引用了实现协议的实际类型,因此我们可以编写以下内容:
protocol P {
typealias ElementType
func foo(c: C<ElementType, Self>) -> ElementType
func foo2() -> ElementType
}
现在我们指定由任何确认类型实现的 foo 函数实际上采用具有指定 ElementType 和实现类型本身的 C。很想,不是吗?
但我们还没有完全完成,最后一个错误仍然存在:
error: <EXPR>:13:18: error: cannot convert the expression's type 'T' to type 'S.ElementType'
return p.foo(self)
此时编译器知道以下内容:
- p.foo(self) 返回以下内容
ElementType
of S
- 函数 bar() 应该返回某种类型
T
但没什么可说的ElementType
and T
实际上是相同的,因此无法确定这是否有效并有抱怨。所以我们真正想要的是ElementType
of S
总是一样T
我们可以指定这一点:
class C<T, S:P where S.ElementType == T> {
完整代码:
protocol P {
typealias ElementType
func foo(c: C<ElementType, Self>) -> ElementType
func foo2() -> ElementType
}
class C<T, S:P where S.ElementType == T> {
var p: S
init (p: S) {
self.p = p
}
func bar() -> T {
return p.foo(self);
}
}