一尘不染

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

swift

我想扩展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()
        }
    }
}

错误消息是“具有约束的’Array’类型的扩展不能具有继承子句”,这似乎最终确定了直接方法的大门。

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

一个可行的解决方案将使此代码编译:

let friendly: Friendly = ["Foo", "Bar"]

更新: 它已落入Swift 4.1中,真是太美了!

extension Array: Friendly where Element: Friendly现在,该示例将按照原始问题中的说明进行编译。


阅读 238

收藏
2020-07-07

共1个答案

一尘不染

编辑:如更新的问题中所述,自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,我的老朋友

2020-07-07