Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/powerbi/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 协议没有';不符合自己?_Swift_Generics_Swift Protocols - Fatal编程技术网

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

为什么这个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 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)