[swift-evolution] Review for: Remove C-style for-loops with conditions and incrementers

Nate Cook natecook at gmail.com
Mon Dec 7 16:20:40 CST 2015

It looks like the standard library has between ten and twenty "for var" instances (depends on if you count before or after de-gybbing), that fall into four categories:

1) for var i = 0; i < length; i++ { ...

These are the most prevalent (and would be everywhere else), and are easily replaced by "for i in 0 ..< length" or "for i in x.indices".

2) for var i = length; --i >= 0; { ...

This is a "count-down" loop, and isn't handled that well in Swift. The "guessing" translation is both wrong and fails to compile ("for i in length ... 0"), another attempt compiles but is easy to get wrong:

for length.stride(to: 0, by: -1) { // wrong, includes length but not zero
for length.stride(through: 0, by: -1) { // wrong, still includes length
for (length - 1).stride(through: 0, by: -1) { // works, but people have switched back to Java by now

Probably the best practice is to use reverse() on the range, since then you'd be using the same method on a "range literal", the indices range, or a collection:

for (0 ..< length).reverse() { ...

3) for var i = 0; i != n && p != limit; i++ { ...

This style *looks* like #1 but hides a second condition -- it's very easy for a newcomer to come to code with this construct and miss second part of the termination case. Far better to refactor this so the second condition is explicit:

for i in 0 ..< n {
 guard p != limit else { break }

4) for var x = foo(y); x.isNotFinished(); x = foo(x) { ...

These can be refactored into while loops without much trouble. Your concern below about continue statements is well-founded. What we might call a "while-defer" loop solves that (though I'm not crazy about the construct):

var x = foo(y)
while x.isNotFinished() {
 defer { x = foo(x) }

That method has the added benefit of putting the test and the "increment" right in one place at the top of the loop. There's a slight difference in that the deferred increment gets executed after a "break", unlike the last statement of a "for var ; ;" loop.


> On Dec 7, 2015, at 3:07 PM, Dmitri Gribenko via swift-evolution <swift-evolution at swift.org> wrote:
> Hi,
> My biggest concern with the proposal is the lack of data regarding
> equivalents for existing C-style for loops.  Note that I'm not talking
> about code that uses C-style for loops in cases where a superior
> construct exists in Swift, for example, `for i in myArray.indices`.
> I'm interested in seeing cases not covered by that.
> For example, it would be good if someone took a look at "git grep 'for
> var' stdlib/" and submitted a PR that converts all those loops.  If
> that results in readability improvements, it would be a great PR
> regardless of the decision on this proposal.
> Another concern of mine is the equivalent of C-style for loops with
> 'continue' statements in them.  The only equivalent based on 'while' I
> can think of duplicates the increment portion of the loop.
> Dmitri
> -- 
> main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
> (j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr at gmail.com>*/
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

More information about the swift-evolution mailing list