From 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 theRangeReplaceableCollection
protocol instead.
However, MutableCollection
requires the following subscript:
subscript(bounds: Range<Self.Index>) -> Self.SubSequence { get set }
Doesn't this allow changing the length of the collection? For instance, couldn't we call this subscript's setter with an empty range and a non-empty subsequence?
Short answer:
If you have a variable of the MutableCollection
type then you must call the subscript setter only with a range
and a new slice having the same length.
Some types conforming to MutableCollection
(such as Array
) allow a different-length replacement to insert or delete elements,
but in general, a mutable collection need not allow that.
In particular, the default implementation of the MutableCollection
subscript setter aborts with a runtime exception if the range and the
new slice do not have the same length.
Longer answer:
First note that you don't have to implement
public subscript(bounds: Range<Index>) -> MutableSlice<Self>
in your own collection because it has a default implementation in a protocol extension. As one can see in the source code of that method, the subscript setter calls a
internal func _writeBackMutableSlice()
function which is implemented here. That function first copies the common number of elements from the slice to the destination range, and then verifies that the subscript range and the new slice have the same length:
_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")
So you cannot change the length of a MutableCollection
via
the (default) subscript setter, and trying to do so will abort the program.
As an example, let use define a "minimal" type conforming to
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
}
}
}
Then replacing a part of the collection with a slice of the same length works:
var mc = MyCollection([0, 1, 2, 3, 4, 5])
mc[1 ... 2] = mc[3 ... 4]
print(mc) // [0, 3, 4, 3, 4, 5]
But for different lengths, it aborts with a runtime exception:
mc[1 ... 2] = mc[3 ... 3]
// fatal error: Cannot replace a slice of a MutableCollection with a slice of a smaller size
Note that concrete types conforming to MutableCollection
may
allow different-lengths replacement in their subscript setter,
as for example Array
does.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With