是否可以向Swift协议一致性扩展添加类型约束?

Pau*_*ell 44 generics swift

我想扩展Array以增加对新协议的一致性 - 但仅适用于其元素本身符合特定协议的数组.

更一般地说,我希望类型参数的类型(无论是协议还是具体类型)仅在类型参数匹配某些约束时才实现协议.

从Swift 2.0开始,这似乎是不可能的.有没有办法让我失踪?

假设我们有Friendly协议:

protocol Friendly {
    func sayHi()
}
Run Code Online (Sandbox Code Playgroud)

我们可以扩展现有类型来实现它:

extension String: Friendly {
    func sayHi() {
        print("Greetings from \(self)!")
    }
}

"Sally".sayHi()
Run Code Online (Sandbox Code Playgroud)

我们还可以扩展Array以实现sayHi()其元素全部Friendly:

extension Array where Element: Friendly {
    func sayHi() {
        for elem in self {
            elem.sayHi()
        }
    }
}

["Sally", "Fred"].sayHi()
Run Code Online (Sandbox Code Playgroud)

此时,类型[Friendly]本身应该实现Friendly,因为它符合协议的要求.但是,此代码无法编译:

extension Array: Friendly where Element: Friendly {
    func sayHi() {
        for elem in self {
            elem.sayHi()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

错误消息是"带有约束的类型'数组'的扩展,不能有继承子句,"这似乎在这种直接方法上明确地关闭了大门.

有间接的解决方法吗?我可以使用一些聪明的技巧?也许有一种方法涉及延伸SequenceType而不是Array

一个有效的解决方案将使这段代码编译:

let friendly: Friendly = ["Foo", "Bar"]
Run Code Online (Sandbox Code Playgroud)

更新:这已经登陆Swift 4.1,它是一个美丽的东西!

extension Array: Friendly where Element: Friendly示例现在编译为原始问题中给出的.

Rob*_*ier 36

这在Swift中是不可能的(从Xcode 7.1开始).如错误所示,您不能将协议一致性("继承子句")限制为类型约束扩展.也许有一天.我不相信这是不可能的,但它目前尚未实施.

您可以获得的最接近的是创建一个包装类型,例如:

struct FriendlyArray<Element: Friendly>: Friendly {
    let array: [Element]
    init(_ array: [Element]) {
        self.array = array
    }
    func sayHi() {
        for elem in array {
            elem.sayHi()
        }
    }
}

let friendly: Friendly = FriendlyArray(["Foo", "Bar"])
Run Code Online (Sandbox Code Playgroud)

(您可能希望扩展FriendlyArrayCollectionType.)

对于我自己下降到试图使这项工作疯狂的故事,以及我从边缘爬回来的故事,请参阅NSData,我的老朋友.

  • 我相信这会到来.https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#conditional-conformances- (5认同)
  • 结构包装器似乎是Swift泛型分解的许多东西的首选解决方案.例如,见证返回`AnySequence`的API方法,因为它们不能返回`SequenceType` .... (2认同)