为何扩展Collection与Array时count返回类型不同?相关疑问
嘿,这个问题问到了Swift集合框架设计的核心细节,咱们一步步捋清楚:
1. 先搞懂两个类型的关系
首先,IndexDistance是Collection协议里定义的关联类型,它的约束是符合SignedInteger(有符号整数)。这个类型的作用是表示「两个索引之间的距离」——而count本质上就是集合的endIndex减去startIndex的结果,所以count的类型自然就是IndexDistance。
而Array作为Collection的具体实现,直接把自己的IndexDistance关联类型固定成了Int。这是因为数组的索引本身就是连续的Int类型,索引之间的距离用Int完全足够,也最符合我们的直觉。
2. 为什么要做这种区分?
Collection是一个通用协议,要适配各种奇奇怪怪的集合实现,不只是数组:
- 比如你可以自定义一个集合,它的索引是自定义的
MyIndex类型(比如基于链表节点的指针、或者分层的位置标记),这时候两个索引之间的距离就不能用Int,得用和MyIndex匹配的差值类型——这时候IndexDistance就派上用场了,它能让count的类型自动适配集合的索引体系。 - 再比如像
String的字符视图,它的索引是String.Index,不是连续的Int(因为Unicode字符的字节长度不固定),这时候IndexDistance依然能正确表示两个索引之间的字符数量,而不用强行转换成Int。
3. 关于数组切片的那番话是什么意思?
你看到的「this[IndexDistance]在数组切片(首个元素的索引不一定为0)中也能正常工作」,其实是在说:
数组切片(ArraySlice)的startIndex不一定是0,比如你取let slice = array[2..<5],这个切片的startIndex是2,endIndex是5。这时候count是endIndex - startIndex = 3,类型是Int(因为ArraySlice的IndexDistance也是Int)。
但如果是一个自定义集合,它的索引不是Int,比如索引是MyIndex(start: 10),切片的startIndex是MyIndex(start: 15),endIndex是MyIndex(start: 20),那count就是endIndex - startIndex,类型是对应的自定义差值类型,依然能正确表示元素的数量——这就是IndexDistance的通用性:不管切片的起始索引是什么,它都能通过索引差值正确计算count,而不用依赖索引必须从0开始。
4. 这个区别是近期变更吗?
不是哦,这个设计从Swift引入Collection协议的早期版本(大概Swift 2左右)就存在了,是集合框架的基础设计之一,目的就是为了让Collection协议能灵活适配各种不同的集合实现。
内容的提问来源于stack exchange,提问作者mfaani

