为什么count会为Collection vs. Array返回不同的类型?

Hon*_*ney 14 arrays collections count associated-types swift

当我伸出Collection的类型countIndexDistance.

当我扩展Array类型时count,类型Int

为什么会有这样的区别?这是最近的变化还是总是像这样?

我已经阅读了这个答案,但不能捡到太多.

我认为唯一相关但却不理解的是:

另一个优点是,[IndexDistance]也可以正确处理数组切片(其中第一个元素的索引不一定为零)

不确定那是什么意思.

我问的原因是为什么代码会在Collection上抛出错误但在Array上没有这样做...尽管两者count最终都是Int.

extension Collection where Element: Comparable{
    func whatever(){
        for index in 0...count{ //  binary operator '...' cannot be applied to operands of type 'Int' and 'Self.IndexDistance'

        }
    }
}

extension Array where Element: Comparable{
    func whatever(){
        for index in 0...count{ // NO ERROR
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

根据Martin和其他人的评论,我添加了一个额外的问题.可能这是我问题的根本原因......

这是否意味着中Collection类型IndexDistance 没有定义Int.基本上一般来说,在"协议"级别,关联类型没有被定义 ......它正在等待具体类型来做到这一点?是对的吗?

话虽如此,是否有任何有意义的用例访问count"协议"级别?我的意思是你无法将它与任何Int它进行比较所以它似乎没用.

Mar*_*n R 10

从Swift编程语言中的关联类型(重点补充):

在定义协议时,将一个或多个关联类型声明为协议定义的一部分有时很有用.关联类型为占用协议一部分的类型提供占位符名称.在采用协议之前,不会指定用于该关联类型的实际类型.使用associatedtype关键字指定关联类型.

斯威夫特3/4.0,Collection协议定义了五种类型的关联(从集合中的哪些内容?):

protocol Collection: Indexable, Sequence {
    associatedtype Iterator: IteratorProtocol = IndexingIterator<Self>
    associatedtype SubSequence: IndexableBase, Sequence = Slice<Self>
    associatedtype Index: Comparable // declared in IndexableBase
    associatedtype IndexDistance: SignedInteger = Int
    associatedtype Indices: IndexableBase, Sequence = DefaultIndices<Self>
    ...
}
Run Code Online (Sandbox Code Playgroud)

这里

    associatedtype IndexDistance: SignedInteger = Int
Run Code Online (Sandbox Code Playgroud)

是一个带有类型约束(: SignedInteger)和默认值(= Int)的关联类型声明,

如果类型T采用协议并且没有T.IndexDistance另外定义,则T.IndexDistance变为类型别名Int.许多标准集合类型(例如ArrayString)都是这种情况,但不适用于所有类型.例如

public struct AnyCollection<Element> : Collection
Run Code Online (Sandbox Code Playgroud)

来自Swift标准库的定义

    public typealias IndexDistance = IntMax
Run Code Online (Sandbox Code Playgroud)

您可以验证

let ac = AnyCollection([1, 2, 3])
let cnt = ac.count
print(type(of: cnt)) // Int64
Run Code Online (Sandbox Code Playgroud)

Int如果您愿意,还可以使用非索引距离定义自己的集合类型:

struct MyCollection : Collection {

    typealias IndexDistance = Int16
    var startIndex: Int { return  0 }
    var endIndex: Int { return  3 }

    subscript(position: Int) -> String {
        return "\(position)"
    }

    func index(after i: Int) -> Int {
        return i + 1
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,如果你扩展的具体类型ArraycountInt:

extension Array {
    func whatever() {
        let cnt = count // type is `Int`
    }
}
Run Code Online (Sandbox Code Playgroud)

但是在协议扩展方法中

extension Collection {
    func whatever() {
        let cnt = count // some `SignedInteger`
    }
}
Run Code Online (Sandbox Code Playgroud)

你知道的一切都是采用协议的某种类型,但不一定cnt这种类型 .当然,人们仍然可以使用伯爵.实际上编译错误了SignedIntegerInt

    for index in 0...count { //  binary operator '...' cannot be applied to operands of type 'Int' and 'Self.IndexDistance'
Run Code Online (Sandbox Code Playgroud)

是误导.整数文字0可以Collection.IndexDistance从上下文中推断为 (因为SignedInteger 符合ExpressibleByIntegerLiteral).但是一系列SignedInteger不是a Sequence,这就是它无法编译的原因.

所以这可行,例如:

extension Collection {
    func whatever() {
        for i in stride(from: 0, to: count, by: 1) {
            // ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 4.1开始, IndexDistance不再使用,并且集合索引之间的距离现在始终表示为a Int,请参阅

特别是返回类型countInt.有一个类型别名

typealias IndexDistance = Int
Run Code Online (Sandbox Code Playgroud)

使旧的代码编译,但这已被弃用,将在未来的Swift版本中删除.