MutableCollection 与 RangeReplaceableCollection

MutableCollection vs RangeReplaceableCollection

来自Apple's MutableCollection API reference

The MutableCollection protocol allows changing the values of a collection’s elements but not the length of the collection itself. For operations that require adding or removing elements, see the RangeReplaceableCollection protocol instead.

但是,MutableCollection需要以下下标:

subscript(bounds: Range<Self.Index>) -> Self.SubSequence { get set }

这不允许更改集合的长度吗?例如,我们不能用一个空范围和一个非空子序列来调用这个下标的 setter 吗?

简答:

如果你有MutableCollection的变量 type 那么你必须调用下标 setter 只有一个范围 和一个具有相同长度的新切片。 一些符合MutableCollection的类型(比如Array)允许一个different-length替换来插入或删除元素, 但一般来说,可变的 collection 不需要这样。

特别是 默认实现 MutableCollection 下标 setter 如果范围和 新切片的长度不同。

更长的答案:

首先请注意,您不必实施

public subscript(bounds: Range<Index>) -> MutableSlice<Self>

在你自己的 collection 因为它在 协议扩展。正如在该方法的 source code 中看到的那样,下标 setter 调用 a

internal func _writeBackMutableSlice()

实现的功能here。 该函数首先从 切片到目标范围,然后验证下标范围和新切片的长度是否相同:

_precondition(
    selfElementIndex == selfElementsEndIndex,
    "Cannot replace a slice of a MutableCollection with a slice of a smaller size")
_precondition(
    newElementIndex == newElementsEndIndex,
    "Cannot replace a slice of a MutableCollection with a slice of a larger size")

所以你不能通过改变 MutableCollection 的长度 (默认)下标 setter,尝试这样做会中止程序。

例如,让我们定义一个 "minimal" 类型符合 MutableCollection:

struct MyCollection : MutableCollection, CustomStringConvertible {

    var storage: [Int] = []

    init(_ elements: [Int]) {
        self.storage = elements
    }

    var description: String {
        return storage.description
    }

    var startIndex : Int { return 0 }
    var endIndex : Int { return storage.count }

    func index(after i: Int) -> Int { return i + 1 }

    subscript(position : Int) -> Int {
        get {
            return storage[position]
        }
        set(newElement) {
            storage[position] = newElement
        }
    }
}

然后用相同长度的切片替换collection的一部分 作品:

var mc = MyCollection([0, 1, 2, 3, 4, 5])
mc[1 ... 2] = mc[3 ... 4]
print(mc) // [0, 3, 4, 3, 4, 5]

但对于不同的长度,它会因运行时异常而中止:

mc[1 ... 2] = mc[3 ... 3]
// fatal error: Cannot replace a slice of a MutableCollection with a slice of a smaller size

注意具体类型符合MutableCollection 可能 允许 different-lengths 替换他们的下标 setter, 例如 Array 就是这样。