I had not as I didn’t really think that the loop index update was that completely general (I really should have as that is just a typically Zig thing …)
However, that seems like it will also break any opportunities for loop unrolling just like putting that inside the block would, no? You don’t really have anything flagging to the compiler “Here! This is the index variable!” like you would if it were explicitly in the while condition.
Given that the update form is so general, though, I’m going to stop worrying about eschewing the “update position” form as it really seems like I’m not losing anything by avoiding it.
The typical use case for defer and errdeferis a simple statement for cleaning up resources. This is perfectly fine and readable, thanks to the keyword. Furthermore, it simplifies code a lot. It’s one of the features I like most.
But you certainly wouldn’t write complex logic in a defer(I mean, something where you have to stop for half a second to understand it).
Compare this to to the while loop with continue expression:
There is no keyword, just the :
You can write the loop easily without it in many cases.
It can be confused with a pre-body condition.
The only situation where it is actually useful is a loop with several continue statements inside, because then you would have to duplicate the if … break for each of them without this feature.
But I think this situation is quite rare. As has been said by others, most while loops use a pre-body /start-of-body condition.
One could as well introduce a feature to check if it’s the first or the last round (some languages have this).
I don’t suggest Zig should do this. The spirit of Zig is to keep the language simple and minimal.
The defer statement is such a useful feature, but the continue expression isn’t IMHO.
Anyway, the OP’s suggestion was to add yet another feature, and we both think this isn’t necessary.
This is mostly an artifact of programming language path dependence rather than an immutable constant or universal truth. In fact, both Knuth and Wirth focused on the do/while form first and demonstrated it as being a strictly stronger control structure.
Our current while loop constructs came later because they made proving things easier precisely because they were less powerful.
My opinion was based around the idea that Zig was doing some useful optimization (like loop unrolling) with the condition and continue. Since it is not, there is no point in me worrying about eschewing the continue part and just keep putting my condition and increment at the bottom of the body like normal.
if...do...while becomes the winner due to having fewer instructions in its loop body. After optimization, the compiler will translate the while statement into if...do...while.
Observing the different outputs of the loop function in Debug and ReleaseFast modes in this example can confirm this point.
With “infamous” I guess you mean “incredibly elegant”?
Seriously though, nothing wrong with the C for-loop syntax. It’s a great fallback when the more ‘specialized’ loop types don’t quite work (similar to how goto is sometimes a much more elegant solution than trying to do the same thing with the ‘higher level’ structured-control-flow syntax).
But I still remember that I found it not intuitive when I learned C in the 80s.
OT:
Colleagues of mine which learned programming languages outside of the C languages family thought that continue means “continue with the stuff after the loop”, aka break
From that point of view (and similarity to other languages is not a primary goal of Zig AFAIK), keywords like loop and exit (instead of break) and next (instead of continue) could be better.
Okay, so attempt-on-failure-try scenarios. Given that failures are often represented by optionals or error unions in Zig, I don’t see how a do-while construct would lead to cleaner code. if ... break inside while (true) {} handle these scenarios reasonably well: