[swift-evolution] [Idea] Replace enumerate() with something more explicit

Dany St-Amant dsa.mls at icloud.com
Sun Apr 17 18:39:10 CDT 2016

> Le 15 avr. 2016 à 17:59, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> a écrit :
> A discussion in the "mapValues" thread reminded me of a longstanding issue I have with Swift.
> `enumerate()` is an attractive nuisance. (That is, it looks like the thing you want, but it's not actually the right one.) Most people use it because they want to enumerate over indices and elements at the same time. In reality, though, the first element of an `enumerate()` tuple is not the index—it's a monotonically increasing integer starting at 0. That *happens* to work for `Array`:
>>    1>     let array = Array(0..<10) 
>>    2.     for (i, elem) in array.enumerate() { 
>>    3.         print(array[i]) 
>>    4.     }
>>  0
>>  1
>>  2
>>  3
>>  4
>>  5
>>  6
>>  7
>>  8
>>  9
> But if you stray even a little bit from the golden path, things start to go wrong:
>>    5>     let range = array[2..<8]
>>    6.     for (i, elem) in range.enumerate() { 
>>    7.         print(range[i])
>>    8.     } 
>>  fatal error: Index out of bounds
> You can scarcely blame users for making this mistake, though—"The Swift Programming Language" encourages the misconception. "Iterating Over an Array" in "Collection Types":
>> If you need the integer index of each item as well as its value, use the `enumerate()` method to iterate over the array instead. For each item in the array, the `enumerate()` method returns a tuple composed of the index and the value for that item.
> While the text is technically accurate—it only talks about iterating over arrays and the numbers generated by `enumerate()` happen to correspond to array indices—it creates a false implication that `enumerate()` is defined to return indices, which isn't true of other collections.
> This is made worse by the fact that `enumerate()` is not really a good name. It is not a common word, so people don't read it and immediately understand what it does; they memorize a Swift-specific meaning, and that meaning may incorporate the misconception that `enumerate()` includes indices. It is also not technically accurate: although it has "number" in its Latin roots, "enumerate" means either "to count" or "to list", not "to number" (i.e. assign numbers to). I know `enumerate()` is used in a couple of other languages (certainly Python, possibly others), but I don't think that overrides the fact that it's confusing.

I see this purely as a documentation issue. It should clearly state the number is not to be used to subscript into the array. Even 'indices' likely warrant a warning that it is pointless to use in some construct such as:

for i in array.filter({ $0 % 2 == 0 }).indices { print("\(i) is not to index array") }

Which of course can be safely be written as

for i in array.indices where array[i] % 2 == 0 { print("\(i) can be used as subscript") }

If we want to have a way to safely subscript into the original array for all possible use cases, we may need a way to "dictionarize" the array.

Providing a new 'enumerated()' which returns the proper array subscript for the simple case where the array is not post-processed (with filter for example) could lead to a false sense of safety when using such construct as subscript into the array.


More information about the swift-evolution mailing list