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

时间:2015-10-25 17:19:48

标签: swift generics

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

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

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

实施例

假设我们有Friendly协议:

protocol Friendly {
    func sayHi()
}

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

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

"Sally".sayHi()

我们还可以Array扩展sayHi(),以便在其元素全部为Friendly时实施extension Array where Element: Friendly { func sayHi() { for elem in self { elem.sayHi() } } } ["Sally", "Fred"].sayHi()

[Friendly]

此时,类型Friendly本身应该实现extension Array: Friendly where Element: Friendly { func sayHi() { for elem in self { elem.sayHi() } } } ,因为它符合协议的要求。 但是,此代码无法编译

SequenceType

错误消息是“带有约束的类型'数组'的扩展名,不能有继承子句,”这似乎直接关闭了直接进场。

是否存在间接解决方法?我可以使用一些聪明的技巧?也许有一种方法涉及延长Array而不是let friendly: Friendly = ["Foo", "Bar"]

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

extension Array: Friendly where Element: Friendly

更新:这已经登陆Swift 4.1,这是一件美丽的事情!

{{1}}示例现在编译原始问题中给出的内容。

2 个答案:

答案 0 :(得分:36)

编辑:如更新后的问题所述,自Swift 4.1以来,现在可以实现这一目标

这在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"])

(您可能希望将FriendlyArray扩展为CollectionType。)

关于我自己下降到试图使这项工作疯狂的故事,以及我从边缘爬回来的故事,请参阅NSData, My Old Friend

答案 1 :(得分:2)

好消息是,你要求Conditional Conformance的内容正在进入Swift 4.1:

https://swift.org/blog/conditional-conformance/