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
}