Swift 协议没有';不符合自己?
为什么这个Swift代码不能编译Swift 协议没有';不符合自己?,swift,generics,swift-protocols,Swift,Generics,Swift Protocols,为什么这个Swift代码不能编译 protocol P { } struct S: P { } let arr:[P] = [ S() ] extension Array where Element : P { func test<T>() -> [T] { return [] } } let result : [S] = arr.test() 协议P{} 结构S:P{} 设arr:[P]=[S()] 扩展数组,其中元素:P{ func
protocol P { }
struct S: P { }
let arr:[P] = [ S() ]
extension Array where Element : P {
func test<T>() -> [T] {
return []
}
}
let result : [S] = arr.test()
协议P{}
结构S:P{}
设arr:[P]=[S()]
扩展数组,其中元素:P{
func test()->[T]{
返回[]
}
}
let结果:[S]=阵列测试()
编译器说:“类型p
不符合协议p
”(或者,在更高版本的Swift中,“不支持使用'p'作为符合协议'p'的具体类型。”)
为什么不呢?不知怎么的,这感觉像是语言上的一个漏洞。我意识到问题源于将数组
arr
声明为协议类型的数组,但是这样做是不合理的吗?我认为协议的存在正是为了帮助结构提供类似类型层次结构的东西?编辑:18个多月的工作w/Swift,另一个主要版本(提供了新的诊断),来自@AyBayBay的评论让我想重写这个答案。新的诊断是:
“不支持将“p”用作符合协议“p”的具体类型。”
这实际上让整个事情变得更清楚了。此扩展:
extension Array where Element : P {
extension Array where Element : P {
func test<T>() -> [T] {
return (self as [P]).test()
}
}
let arr = [S()]
let result: [S] = arr.test()
当元素==p
时不适用,因为p
不被视为p
的具体一致性。(下面的“放入盒子”解决方案仍然是最普遍的解决方案。)
旧答案: 这是元类型的又一个例子。斯威夫特真的希望你得到一个具体的类型为大多数非琐碎的事情<代码>[P]不是具体类型(您不能为
P
分配已知大小的内存块)。(我不认为这是真的;你完全可以创造一些大小P
的东西,因为。)我不认为有任何证据表明这是一个“不应该”的案例。这看起来很像他们的一个“还不起作用”的案例。(不幸的是,要让苹果公司确认这两种情况之间的差异几乎是不可能的。)事实上数组
可以是一种变量类型(其中数组
不能),这表明他们已经在这方面做了一些工作,但Swift元类型有很多尖锐的边缘和未实现的情况。我不认为你会得到比这个更好的“为什么”答案。“因为编译器不允许。”(不满意,我知道。我的整个快速生活…)
解决办法几乎总是把东西放在盒子里。我们制作了一个打字机橡皮擦
protocol P { }
struct S: P { }
struct AnyPArray {
var array: [P]
init(_ array:[P]) { self.array = array }
}
extension AnyPArray {
func test<T>() -> [T] {
return []
}
}
let arr = AnyPArray([S()])
let result: [S] = arr.test()
协议P{}
结构S:P{}
结构AnyPArray{
变量数组:[P]
init({array:[P]){self.array=array}
}
分机{
func test()->[T]{
返回[]
}
}
设arr=AnyPArray([S()]))
let结果:[S]=阵列测试()
当Swift允许您直接这样做时(我确实希望最终会这样),它很可能只是自动为您创建这个框。递归枚举正是这种历史。你不得不将它们框起来,这是令人难以置信的恼人和限制,最后编译器添加了
indirect
,以便更自动地完成同样的事情。EDIT:18个多月的工作w/Swift,另一个主要版本(提供了新的诊断),来自@AyBayBay的评论让我想重写这个答案。新的诊断是:
“不支持将“p”用作符合协议“p”的具体类型。”
这实际上让整个事情变得更清楚了。此扩展:
extension Array where Element : P {
extension Array where Element : P {
func test<T>() -> [T] {
return (self as [P]).test()
}
}
let arr = [S()]
let result: [S] = arr.test()
当元素==p
时不适用,因为p
不被视为p
的具体一致性。(下面的“放入盒子”解决方案仍然是最普遍的解决方案。)
旧答案: 这是元类型的又一个例子。斯威夫特真的希望你得到一个具体的类型为大多数非琐碎的事情<代码>[P]不是具体类型(您不能为
P
分配已知大小的内存块)。(我不认为这是真的;你完全可以创造一些大小P
的东西,因为。)我不认为有任何证据表明这是一个“不应该”的案例。这看起来很像他们的一个“还不起作用”的案例。(不幸的是,要让苹果公司确认这两种情况之间的差异几乎是不可能的。)事实上数组
可以是一种变量类型(其中数组
不能),这表明他们已经在这方面做了一些工作,但Swift元类型有很多尖锐的边缘和未实现的情况。我不认为你会得到比这个更好的“为什么”答案。“因为编译器不允许。”(不满意,我知道。我的整个快速生活…)
解决办法几乎总是把东西放在盒子里。我们制作了一个打字机橡皮擦
protocol P { }
struct S: P { }
struct AnyPArray {
var array: [P]
init(_ array:[P]) { self.array = array }
}
extension AnyPArray {
func test<T>() -> [T] {
return []
}
}
let arr = AnyPArray([S()])
let result: [S] = arr.test()
协议P{}
结构S:P{}
结构AnyPArray{
变量数组:[P]
init({array:[P]){self.array=array}
}
分机{
func test()->[T]{
返回[]
}
}
设arr=AnyPArray([S()]))
let结果:[S]=阵列测试()
当Swift允许您直接这样做时(我确实希望最终会这样),它很可能只是自动为您创建这个框。递归枚举正是这种历史。你不得不把它们框起来,这是令人难以置信的恼人和限制,最后编译器添加了
间接
,以便更自动地完成同样的事情。如果你扩展收集类型
协议,而不是数组
和协议约束作为具体类型,您可以按如下方式重写前面的代码
protocol P { }
struct S: P { }
let arr:[P] = [ S() ]
extension CollectionType where Generator.Element == P {
func test<T>() -> [T] {
return []
}
}
let result : [S] = arr.test()
协议P{}
结构S:P{}
设arr:[P]=[S()]
扩展集合类型,其中Generator.Element==P{
func test()->[T]{
返回[]
}
}
let结果:[S]=阵列测试()
如果您扩展了CollectionType
协议,而不是Array
和constraint by protocol作为一个具体类型,那么您可以如下重写前面的代码
protocol P { }
struct S: P { }
let arr:[P] = [ S() ]
extension CollectionType where Generator.Element == P {
func test<T>() -> [T] {
return []
}
}
let result : [S] = arr.test()
协议P{}
结构S:P{}
设arr:[P]=[S()]
扩展集合类型,其中Generator.Element==P{
func test()->[T]{
extension Array where Element == P {
func test<T>() -> [T] {
return []
}
}
let arr: [P] = [S()]
let result: [S] = arr.test()
extension Array where Element : P {
func test<T>() -> [T] {
return (self as [P]).test()
}
}
let arr = [S()]
let result: [S] = arr.test()
protocol P {
var bar: Int { get set }
func foo(str: String)
}
struct S : P {
var bar: Int
func foo(str: String) {/* ... */}
}
func takesConcreteP<T : P>(_ t: T) {/* ... */}
let p: P = S(bar: 5)
// error: Cannot invoke 'takesConcreteP' with an argument list of type '(P)'
takesConcreteP(p)
extension P {
func callTakesConcreteP/*<Self : P>*/(/*self: Self*/) {
takesConcreteP(self)
}
}
p.callTakesConcreteP()
struct Q : P {
var bar: Int
func foo(str: String) {}
}
// The placeholder `T` must be satisfied by a single type
func takesConcreteArrayOfP<T : P>(_ t: [T]) {}
// ...but an array of `P` could have elements of different underlying concrete types.
let array: [P] = [S(bar: 1), Q(bar: 2)]
// So there's no sensible concrete type we can substitute for `T`.
takesConcreteArrayOfP(array)
struct AnyP : P {
private var base: P
init(_ base: P) {
self.base = base
}
var bar: Int {
get { return base.bar }
set { base.bar = newValue }
}
func foo(str: String) { base.foo(str: str) }
}
let p = AnyP(S(bar: 5))
takesConcreteP(p)
// example from #1...
let array = [AnyP(S(bar: 1)), AnyP(Q(bar: 2))]
takesConcreteArrayOfP(array)