当我扩展 Collection
时,count
的类型是 IndexDistance
。
当我扩展 Array
类型时,count
是 Int
类型
为什么会有这样的区分?这是最近的变化还是一直如此?
我读过这个answer但收不到太多。
唯一我认为相关但不理解的是:
Another advantage is that this[IndexDistance] also works correctly with array slices (where the index of the first element is not necessarily zero
不知道是什么意思。
我问的原因是,为什么代码在 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
}
}
}
编辑:
根据 Martin 和其他人的评论,我添加了一个额外的问题。可能这是我问题的根本原因......
这是否意味着在 Collection
中键入 IndexDistance
未定义为 Int
。基本上一般来说,在“协议(protocol)”级别,associatedTypes 未定义...它正在等待一个具体类型来做到这一点?那正确吗?
也就是说,在“协议(protocol)”级别访问 count
是否有任何有意义的用例?我的意思是您无法将它与任何 Int
进行比较,因此它看起来毫无用处。
最佳答案
来自 Associated Types在 Swift 编程语言中(添加了重点):
When defining a protocol, it’s sometimes useful to declare one or more associated types as part of the protocol’s definition. An associated type gives a placeholder name to a type that is used as part of the protocol. The actual type to use for that associated type isn’t specified until the protocol is adopted. Associated types are specified with the associatedtype keyword.
在Swift 3/4.0中,Collection
协议(protocol)定义了五种关联类型
(来自 What’s in a 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>
...
}
这里
associatedtype IndexDistance: SignedInteger = Int
是一个带有类型约束(: SignedInteger
)和默认值(= Int
)的关联类型声明,
如果类型 T
采用协议(protocol)并且没有定义 T.IndexDistance
否则 T.IndexDistance
成为 的类型别名>整数
。
许多标准集合类型都是这种情况
(例如 Array
或 String
),但不是所有的。例如
public struct AnyCollection<Element> : Collection
来自 Swift 标准库定义
public typealias IndexDistance = IntMax
你可以用它来验证
let ac = AnyCollection([1, 2, 3])
let cnt = ac.count
print(type(of: cnt)) // Int64
如果愿意,您还可以使用非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
}
}
因此,如果您扩展具体类型Array
,那么count
是一个Int
:
extension Array {
func whatever() {
let cnt = count // type is `Int`
}
}
但是在协议(protocol)扩展方法中
extension Collection {
func whatever() {
let cnt = count // some `SignedInteger`
}
}
你所知道的是 cnt
的类型是 some 采用
SignedInteger
协议(protocol),但不一定是 Int
。一个还可以
当然是和伯爵一起工作。其实编译错误在
for index in 0...count { // binary operator '...' cannot be applied to operands of type 'Int' and 'Self.IndexDistance'
具有误导性。整数文字 0
可以推断为
Collection.IndexDistance
来自上下文(因为 SignedInteger
符合 ExpressibleByIntegerLiteral
)。但是 SignedInteger
的范围不是 Sequence
,这就是编译失败的原因。
所以这会起作用,例如:
extension Collection {
func whatever() {
for i in stride(from: 0, to: count, by: 1) {
// ...
}
}
}
自 Swift 4.1 IndexDistance
不再使用,并且
集合索引之间的距离现在总是表示为 Int
,请参阅
特别是 count
的返回类型是 Int
。有一个类型别名
typealias IndexDistance = Int
使旧代码编译,但已被弃用并将被删除 在 Swift 的 future 版本中。
关于arrays - 为什么 count 返回 Collection 和 Array 的不同类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49540080/