如何约束函数参数的协议关联类型

How to constrain function parameter's protocol's associated types

为了好玩,我正在尝试扩展词典 class 以复制 Python 的计数器 class。我正在尝试实施 init,将 CollectionType 作为唯一参数。但是,由于 CollectionType 的关联类型,Swift 不允许这样做。所以,我正在尝试编写这样的代码:

import Foundation

// Must constrain extension with a protocol, not a class or struct
protocol SingletonIntProtocol { }
extension Int: SingletonIntProtocol { }

extension Dictionary where Value: SingletonIntProtocol { // i.e. Value == Int
    init(from sequence: SequenceType where sequence.Generator.Element == Key) {
        // Initialize
    }   
}

但是,Swift 不允许在参数列表中使用此语法。有没有办法写 init 以便它可以采用符合 CollectionType 的任何类型,其值是 Key 类型(通用 Dictionary<Key: Hashable, Value> 中使用的类型的名称)?最好不要强迫我写 init(from sequence: [Key]),这样我就可以取任何 CollectionType(比如 CharacterView)。

您只是遇到了语法问题。你的基本想法似乎不错。正确的语法是:

init<Seq: SequenceType where Seq.Generator.Element == Key>(from sequence: Seq) {

这个答案的其余部分只是解释了为什么语法是这样的。如果第一部分让你满意,你就真的不需要阅读其余部分了。

细微差别在于您试图将 SequenceType where sequence.Generator.Element == Key 视为一种类型。这不是一种类型;这是一个类型约束。正确的语法是:

There is a type Seq such that Seq.Generator.Element == Key, and sequence must be of that type.

虽然这看起来是一回事,但不同之处在于 Seq 在任何给定时间都是一种特定类型。它不是 "any type that follows this rule." 它实际上是一种特定类型。每次你用某种类型(比如 [Key])调用 init 时,Swift 将创建一个全新的 init 方法,其中 Seq 被替换为 [Key]. (事实上​​ ,Swift有时可以优化那个额外的方法,但原则上它存在。)这是理解通用语法的关键点。

或者你可以只记住尖括号的位置,让编译器在你搞砸的时候提醒你,然后就到此为止吧。大多数人在不学习支撑它的类型理论的情况下都做得很好。