MutableCollection与RangeReplaceableCollection

Tim*_*len 6 swift swift3

来自Apple的MutableCollectionAPI参考:

MutableCollection协议允许更改集合元素的值,但不能更改集合本身的长度.对于需要添加或删除元素的操作,请参阅RangeReplaceableCollection协议.

但是,MutableCollection需要以下下标:

subscript(bounds: Range<Self.Index>) -> Self.SubSequence { get set }
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*n R 5

简短回答:

如果您有一个MutableCollection 类型的变量,那么您必须仅使用范围和具有相同长度的新切片调用下标setter.符合MutableCollection(例如Array)的某些类型允许插入或删除元素的不同长度替换,但通常,可变集合不需要允许.

特别是, 如果范围和新切片的长度不同,则下标setter 的默认实现MutableCollection将以运行时异常中止.

更长的回答:

首先请注意,您不必实施

public subscript(bounds: Range<Index>) -> MutableSlice<Self>
Run Code Online (Sandbox Code Playgroud)

在您自己的集合中,因为它在协议扩展中具有默认实现.正如可以在该方法的源代码中看到的那样,下标setter调用a

internal func _writeBackMutableSlice()
Run Code Online (Sandbox Code Playgroud)

这里实现的功能.该函数首先将通用数量的元素从切片复制到目标范围,然后验证下标范围和新切片具有相同的长度:

_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")
Run Code Online (Sandbox Code Playgroud)

所以你不能改变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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后用相同长度的切片替换集合的一部分:

var mc = MyCollection([0, 1, 2, 3, 4, 5])
mc[1 ... 2] = mc[3 ... 4]
print(mc) // [0, 3, 4, 3, 4, 5]
Run Code Online (Sandbox Code Playgroud)

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

mc[1 ... 2] = mc[3 ... 3]
// fatal error: Cannot replace a slice of a MutableCollection with a slice of a smaller size
Run Code Online (Sandbox Code Playgroud)

注意,符合的具体类型MutableCollection 可以 允许在其下标设定器中进行不同长度的替换,例如Array.