protocol P { typealias T func foo(c: C<T>) -> T func foo2() -> T } class C<T> { var p: P init (p: P) { self.p = p } func bar() -> T { return p.foo(self); } }
协议P定义了与任何专门的C类型正确匹配的关联类型.我错过了什么吗?或不?
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’只能用作通用约束,因为它具有自我或相关类型要求”.
所以问题是相关联的类型.在当前配置中,您指定了初始化程序和变量的参数类型为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,但是我们不能:
: 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
由于P未指定相关类型的ElementType,它不正确符合P,并且不能在符合P类型的地方使用.但有一个很好的特殊类型:自我.这引用了实现协议的实际类型,所以我们可以写下列内容:
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)返回S的ElementType
>函数bar()应返回T类型
但是没有什么可以告诉的,ElementType和T实际上是一样的,所以它不能确定这是否有效和抱怨.所以我们实际想要的是,S的ElementType总是和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); } }