Arrays 为什么计数返回集合与数组的不同类型?

Arrays 为什么计数返回集合与数组的不同类型?,arrays,swift,collections,count,associated-types,Arrays,Swift,Collections,Count,Associated Types,当我扩展Collection时,count的类型是IndexDistance 当我扩展Array类型时,count类型为Int 为什么会有这样的区别?这是最近的变化还是一直如此 我读过这篇文章,但没有多少收获 我认为唯一相关但不明白的是: 另一个优点是[IndexDistance]也能正常工作 使用数组切片(其中第一个元素的索引不是 必然零 不知道那是什么意思 我要问的原因是,为什么代码会在集合上抛出错误,但在数组上却没有这样做……即使两个counts最终都是Int extension Coll

当我扩展
Collection
时,
count
的类型是
IndexDistance

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

为什么会有这样的区别?这是最近的变化还是一直如此

我读过这篇文章,但没有多少收获

我认为唯一相关但不明白的是:

另一个优点是[IndexDistance]也能正常工作 使用数组切片(其中第一个元素的索引不是 必然零

不知道那是什么意思

我要问的原因是,为什么代码会在集合上抛出错误,但在数组上却没有这样做……即使两个
count
s最终都是
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和其他人的评论,我添加了一个额外的问题。这可能是我问题的根本原因

这是否意味着在
集合
类型中,
索引距离
未定义为
Int
。基本上在“协议”级别上,关联类型未定义…它正在等待一个具体类型来定义它?是这样吗

也就是说,在“协议”级别访问
count
是否有任何有意义的用例?我的意思是,您无法将其与任何
Int
进行比较,因此它似乎毫无用处。

来自于Swift编程语言(重点添加):

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

在Swift 3/4.0中,
收集协议定义了五种相关类型 (发件人):

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

如果类型
T
采用协议,并且没有定义
T.IndexDistance
否则,
T.IndexDistance
将成为
Int
的类型别名。 许多标准集合类型都是这样 (例如
数组
字符串
),但不是所有的

public struct AnyCollection<Element> : Collection
你可以用它来验证

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
    }
}
因此,如果扩展具体类型
数组
,则
计数
是一个
Int

extension Array {
    func whatever() {
        let cnt = count // type is `Int`
    }
}
但是在协议扩展方法中

extension Collection {
    func whatever() {
        let cnt = count // some `SignedInteger`
    }
}
您所知道的一切是,
cnt
的类型是采用
SignedInteger
协议,但不必是
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
的范围不是
序列,这就是它无法编译的原因

因此,这将起作用,例如:

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的未来版本中。

不完全是一个答案,但作为OP,我认为这些都是我理解的重要前提。我不知道:

  • 您可以约束协议的
    associatedtype
  • 您可以为
    associatedtype
    指定默认类型
  • 可以通过使用
    typealias
    来实现与协议的
    关联类型的一致性
  • 与协议的
    关联类型
    的一致性也可以通过其他方式(即通过默认方式)实现
  • 根据设计关联类型
的默认类型不会“在协议级别”触发,即它只会被约束到其受约束的类型。但是,一旦类/结构采用了该类型,则只会使用默认类型。有关更多信息,请参阅上文和上的Apple文档
  • 还有第三种方法符合协议的
    associatedtype
    。请查看最后提供的链接。基本上,您可以通过隐式定义
    associatedtype
    来符合
  • 也许最常见的方法是通过泛型约束符合具有关联类型的协议。请参见
    SomeClass9

  • 三种不同的协议
    与协议的一致性
    //一切正常
    类someClass1:不受约束{
    typealias IndexDistance=Int
    }
    //一切都好
    类someClass2:不受约束{
    typealias IndexDistance=String//它也适用于String,因为它不受约束
    }
    //不好
    类SomeClass3:不受约束{
    //错误:类型“SomeClass3”不符合协议“NotConstrained”
    //不起作用,因为我们必须有一个typealias
    }
    //一切都好
    类SomeClass4:受约束{
    typealias IndexDistance=Int16
    }
    //不好
    类SomeClass5:受约束{
    typealias IndexDistance=字符串
    //错误:类型“SomeClass5”不符合协议“Constrained”
    //显然!因为字符串不是“SignedGeter”类型
    }
    //不好
    类SomeClass6:约束{
    //错误:键入“SomeClass6”
    
        for index in 0...count { //  binary operator '...' cannot be applied to operands of type 'Int' and 'Self.IndexDistance'
    
    extension Collection {
        func whatever() {
            for i in stride(from: 0, to: count, by: 1) {
                // ...
            }
        }
    }
    
    typealias IndexDistance = Int
    
    // associatedtype isn't constrained
    protocol NotConstrained{
        associatedtype IndexDistance
    }
    
    // associatedtype is constrained
    protocol Constrained{
        associatedtype IndexDistance: SignedInteger
    }
    
    // associatedtype is constrained and defaulted
    protocol ConstrainedAndDefaulted{
        associatedtype IndexDistance: SignedInteger = Int
    }
    
    // All Good
    class someClass1: NotConstrained{
        typealias IndexDistance = Int
    }
    
    // All Good
    class someClass2: NotConstrained{
        typealias IndexDistance = String // It works with String as well, since it wasn't constrained
    }
    
    // Not Good
    class SomeClass3: NotConstrained{
        // error: type 'SomeClass3' does not conform to protocol 'NotConstrained'
        // doesn't work because we MUST have a typealias
    }
    
    // All Good
    class SomeClass4: Constrained{
        typealias IndexDistance = Int16
    }
    
    // Not Good
    class SomeClass5: Constrained{
        typealias IndexDistance = String
        // error: type 'SomeClass5' does not conform to protocol 'Constrained'
        // Obviously! Because String isn't of type 'SignedIngeter'
    }
    
    // Not Good
    class SomeClass6: Constrained{
        // error: type 'SomeClass6' does not conform to protocol 'Constrained'        
    }
    
    // All Good
    class SomeClass7: ConstrainedAndDefaulted{
        // NO ERROR, because the associatedtype has already defaulted
    }
    
    // All Good
    class SomeClass8: ConstrainedAndDefaulted{
        typealias IndexDistance = Int64 // We changed the default from 'Int' to 'Int64'
        // Which is ok because 'Int64' is of type 'SignedInteger'
    }
    
    class SomeClass9<T> : NotConstrained {
        typealias IndexDistance = T
    }