I'm new to Swift and I'm exploring what happens if a collection is modified while iterating over it using a Range. I can't figure out why these two loops have different behavior:
var things = ["a", "b", "c"]
for i in 0..<things.count {
print("i: \(i) count: \(things.count) value: \(things[i])")
if i == 1 {
things.append("x")
}
}
// i: 0 count: 3 value: a
// i: 1 count: 3 value: b
// i: 2 count: 4 value: c
vs
var things = ["a", "b", "c"]
for i in things.startIndex...things.endIndex {
print("i: \(i) count: \(things.count) value: \(things[i])")
if i == 1 {
things.append("x")
}
}
// i: 0 count: 3 value: a
// i: 1 count: 3 value: b
// i: 2 count: 4 value: c
// i: 3 count: 4 value: x
I created this Array class extension so I could see when the Range literal accesses the array's properties:
extension Array {
var myCount: Int {
print("myCount (\(self.count))")
return self.count
}
var myStartIndex: Int {
print("myStartIndex (\(self.startIndex))")
return self.startIndex
}
var myEndIndex: Int {
print("myEndIndex (\(self.endIndex))")
return self.endIndex
}
}
If I use these properties instead of the normal ones, I get:
myCount (3)
i: 0 count: 3 value: a
i: 1 count: 3 value: b
i: 2 count: 4 value: c
and
myStartIndex (0)
myEndIndex (3)
i: 0 count: 3 value: a
i: 1 count: 3 value: b
i: 2 count: 4 value: c
i: 3 count: 4 value: x
I must be missing something, because this feels like magic! It seems like endIndex is being re-evaluated, but count is not. What gives?
for i in things.indices {or if you don't need the indexfor thing in things {