1

I just updated XCode to 7.3 and had a surprise about the number of warnings of my code, especially this one:

C-style for statement is deprecated and will be removed in a future version of Swift 

I saw some solutions:

for idx in 0...<10 {}

or

for idx in 10.stride(to: 0, by: -1)

Seriously, why? Would it be better using stride then the C-style for loop? What are the advantages? I am quite confused when using the for-loop now. I have to examine repeatedly to see if I am using a for-loop correctly.

Joe Huang
  • 6,296
  • 7
  • 48
  • 81
  • 1
    Just google it. There was a quote of the justification for removing it here... https://medium.com/@swiftafricanus/swift-c-style-loops-deprecated-ab796d235298#.7z476wm0k – Fogmeister Mar 23 '16 at 13:55
  • Swift never really implemented the 'C-Style' for loop, even though the syntax suggested it did - the loop index did not behave in the way one would expect from C (i.e. it wasn't a variable) - see this question/answer http://stackoverflow.com/questions/33219684/cannot-assign-to-value-i-is-a-let-constant-in-swift/33219795#33219795 – Grimxn Mar 23 '16 at 13:56
  • 1
    @vadian `C-style for loop` is not the same as increment/decrement operators. – Sulthan Mar 23 '16 at 13:58
  • @SulthanYes, but both issues are related – vadian Mar 23 '16 at 14:05
  • @vadian Only because they come in the same Xcode release and because they both deprecate C-style. They are not duplicates though. – Sulthan Mar 23 '16 at 14:07
  • @Grimxn: In a C-style for-loop `for (var i = 0; i < 10; i = i + 1)` the loop index *is* a variable and can be modified within the loop body. – Martin R Mar 23 '16 at 14:10
  • 2
    This is not a duplicate of http://stackoverflow.com/questions/35158422/the-and-operators-have-been-deprecated-xcode-7-3, therefore I have reopened the question. It might qualify as opinion-based though. – Martin R Mar 23 '16 at 14:12
  • @MartinR - I know, but the questioner I referenced (and, I suspect, most others learning the language) did not - with out the `var` (i.e. using C syntax) it does not behave as C does. – Grimxn Mar 23 '16 at 14:13
  • As a side note - If you're ever trying to loop through an array without using a for each loop then use: for i in array.indices { } – Ben Sullivan Mar 23 '16 at 14:23
  • I still think c style loop should exist for functionality. The all other swift loops cannot edit value of index that is being iterated - like in strides, you cannot edit the i while iterating. Here a classic mistake by creating a new thing - destroying a good thing of the past without a full replacement. – coolcool1994 May 15 '21 at 04:46

1 Answers1

8

For details, see Swift Evolution - Remove C style for-loops

To quote the reasoning:

  1. Both for-in and stride provide equivalent behavior using Swift-coherent approaches without being tied to legacy terminology.
  2. There is a distinct expressive disadvantage in using for-loops compared to for-in in succinctness
  3. for-loop implementations do not lend themselves to use with collections and other core Swift types.
  4. The for-loop encourages use of unary incrementors and decrementors, which will be soon removed from the language.
  5. The semi-colon delimited declaration offers a steep learning curve from users arriving from non C-like languages
  6. If the for-loop did not exist, I doubt it would be considered for inclusion in Swift 3.

In summary: there are better ways (more expressive) than a C-style for-loop to iterate in Swift.

Some examples:

for-in over a range:

for i in 0 ..< 10 {
    //iterate over 0..9
    print("Index: \(i)")
}

for i in (0 ..< 10).reverse() {
    //iterate over 9..0
    print("Index: \(i)")
}

For arrays (and other sequences) we have many options (the following is not a complete list):

let array = ["item1", "item2", "item3"]

array.forEach {
    // iterate over items
    print("Item: \($0)")
}

array.reverse().forEach {
    // iterate over items in reverse order
    print("Item: \($0)")
}

array.enumerate().forEach {
    // iterate over items with indices
   print("Item: \($1) at index \($0)")
}

array.enumerate().reverse().forEach {
    // iterate over items with indices in reverse order
    print("Item: \($1) at index \($0)")
}

for index in array.indices {
    // iterate using a list of indices
    let item = array[index]
    print("Item \(item) at index: \(index)")
}

Also note that if you are converting an array to another array, almost always you want to use array.filter or array.map or a combination of them.

For all Strideable types we can use the stride method to generate indices, for example:

for index in 10.stride(to: 30, by: 5) {
    // 10, 15, 20, 25 (not 30)
    print("Index: \(index)")
} 

for index in 10.stride(through: 30, by: 5) {
    // 10, 15, 20, 25, 30
    print("Index: \(index)")
}

With arrays we can do:

for index in 0.stride(to: array.count, by: 2) {
    // prints only every second item
    let item = array[index]
    print("Item \(item) at index: \(index)")
}
Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • How to solve the case if the index is changed within the for loop block? – Joe Huang Mar 23 '16 at 14:17
  • 2
    @JoeHuang Changing index in the for-loop is an antipattern and always has been, even in C/C++/Java. The best solution depends on your use case but there is is always a `while { ... }` loop. – Sulthan Mar 23 '16 at 14:19
  • 2
    Don't forget .stride(to:by:) when the inc/decrementer is not 1. – John Difool Mar 23 '16 at 14:55
  • After I changed all the for-loop statements into `for index in 0..some_count`, I found my app crashes sometimes because `Can't form Range with end < start` if `some_count` is negative during execution. I start to miss the old loop statement. Now I have to use different kinds of loops to take care all possibilities. – Joe Huang Mar 27 '16 at 06:43
  • 2
    @JoeHuang The problem with the old `for-loop` is that it swallows corner cases. The new alternatives force you to be more expressive, solving all corner cases explicitly. This makes your code more readable and avoids some bugs before they even happen. If in a range `start<..end` the `end` is lower then `start`, then it's a corner case and from your code it should be immediately visible that you know about that corner case. – Sulthan Mar 27 '16 at 08:43
  • You can't change the value of i that you are using to iterate mid way through though. C for loops can. That's the drawback. Swift for loop has this defect. You need to use while loop in those use cases .. – coolcool1994 May 15 '21 at 04:39
  • @coolcool1994 thats not a drawback. You should never change the value of the iterator, even in C. If you want to do it then you *should* be using a `while` loop. – Sulthan May 15 '21 at 05:11