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允许您直接这样做时(我确实希望最终会这样),它很可能只是自动为您创建这个框。递归枚举正是这种历史。你不得不把它们框起来,这是令人难以置信的恼人和限制,最后编译器添加了
间接
,以便更自动地完成同样的事情。如果你扩展收集类型
协议,而不是数组
和协议约束作为具体类型,您可以按如下方式重写前面的代码
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]=阵列测试()
为什么协议不符合自身要求?
允许协议在一般情况下符合自身是不合理的。问题在于静态协议需求
这些措施包括:
方法和属性静态
- 初始化器
- 关联类型(尽管这些类型当前阻止将协议用作实际类型)
T
上访问这些需求,其中T:p
——但是我们不能在协议类型本身上访问它们,因为没有具体的一致性类型可以转发。因此,我们不能允许T
成为P
考虑一下,如果我们允许数组
扩展适用于[p]
,在下面的示例中会发生什么情况:
protocol P {
init()
}
struct S : P {}
struct S1 : P {}
extension Array where Element : P {
mutating func appendNew() {
// If Element is P, we cannot possibly construct a new instance of it, as you cannot
// construct an instance of a protocol.
append(Element())
}
}
var arr: [P] = [S(), S1()]
// error: Using 'P' as a concrete type conforming to protocol 'P' is not supported
arr.appendNew()
我们不可能在[p]
上调用appendNew()
,因为p
(元素
)不是具体类型,因此无法实例化。必须在具有具体类型元素的数组上调用它,其中该类型符合P
这与静态方法和属性要求类似:
protocol P {
static func foo()
static var bar: Int { get }
}
struct SomeGeneric<T : P> {
func baz() {
// If T is P, what's the value of bar? There isn't one – because there's no
// implementation of bar's getter defined on P itself.
print(T.bar)
T.foo() // If T is P, what method are we calling here?
}
}
// error: Using 'P' as a concrete type conforming to protocol 'P' is not supported
SomeGeneric<P>().baz()
baz
要求T
符合p
;但是我们可以用P
代替T
,因为P
没有静态需求。如果我们将静态需求添加到P
,则示例不再编译:
import Foundation
@objc protocol P {
static func bar()
func foo()
}
class C : P {
static func bar() {
print("C's bar called")
}
func foo() {
print("C's foo called!")
}
}
func baz<T : P>(_ t: T) {
t.foo()
}
let c: P = C()
baz(c) // error: Cannot invoke 'baz' with an argument list of type '(P)'
添加@objc
使其可编译;删除它会使它不再编译。
我们中的一些人对堆栈溢出感到惊讶,并希望
要知道这是故意的还是有问题的
Slava Pestov添加了一条评论-2017年9月7日下午1:53
这是故意的——解除这个限制就是这个bug的目的。
就像我说的,这很棘手,我们还没有任何具体的计划
所以希望有一天,这种语言也能支持非@objc协议
但是,对于非@objc协议,目前有哪些解决方案
利用协议约束实现扩展 在Swift 3.1中,如果您希望扩展具有一个约束,即给定的通用占位符或关联类型必须是给定的协议类型(而不仅仅是符合该协议的具体类型)——您可以简单地使用
=
约束来定义它
例如,我们可以将您的数组扩展编写为:
extension Array where Element == P {
func test<T>() -> [T] {
return []
}
}
let arr: [P] = [S()]
let result: [S] = arr.test()
但是值得注意的是,这将执行O(n)转换
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)