I usually use Zig’s for loops for most things, but sometimes I need to use traditional C-style loops with an initialization step. Here’s an example from something I’m working on:
{var i: u8 = 0; while (true) : (i += 1) {
// ...
}}
I don’t want to increase the indentation level just for one variable, so I usually resort to the above. I think it looks ugly. I think that the current for should be renamed to foreach, and the new for should be like C’s for loops:
Adding a new feature just to save a few lines of code isn’t really what Zig is interested in in my experience. Note also that for loops that work on ranges (e.g. for (0..end_index) |i| {) only got added as a side-effect of a different change with other use cases in mind: Proposal: Multi-object `for` loops · Issue #7257 · ziglang/zig · GitHub
I have thought about this many times, the C style for loops is definitely one of the things I miss in Zig. However every time I think about it, I notice that a hypothetical Zig version is just too verbose and doesn’t match the simplicity of the C-style for loop:
for(var i: i32 = 0; i < len; i += 1)
for(int i = 0; i < len; i++)
This is even more true for your proposal, which adds colons everywhere and requires even more upfront knowledge.
I personally think that a syntax involving captures (similar to the current for) would fit best in the language, since that would make the index immutable by default. But so far I haven’t found anything good enough for a proposal.
I think many do, especially when nesting, and frequently end up not doing it, and instead reuse variables in the same scope. There are many examples in the wild, which I think this is pretty brittle in particular when refactoring.
It’s not about saving lines of code, it’s about preventing the index variable from leaking to the outer scope. The fact that the only way to do this in current Zig is ugly means that a lot of people just won’t do it.
Use anonymous blocks to limit the scope of variables.
(slightly interestingly, this aspect of Zig has gotten me doing the same thing in other programming languages, e.g. when I’m writing Lua now I find myself writing
do
-- stuff
end
somewhat often, which I never really did before Zig)
This is more more verbose and significantly harms readability. As mentioned by @cryptocode, many choose to not do it. You can get around the problem sure, but that doesn’t mean the problem doesn’t exist.
Is the inner index variable always the only one which is specific to a while loop? No, right? So with anonymous blocks, all of those gets scoped. With for statements, it’s easier to just set them up before the loop, then they leak when that loop is over.
I don’t think it’s ugly at all, and have always found this kind of argument to be weak advocacy for a position.
I like that the language is made of fully fleshed out primitives which compose well together. Sugar is bad for your liver.
Where u8.{0..16} defines a special span type that for() knows how to make use of. The advantage here is that i would be const not var, eliminating potential misunderstanding due to unexpected modification of the index within the loop.
The theoretical construct would allow you to loop through multiple spans:
Just use a range.
for the second example an array of start and end of the ranges
I know is always usize, but you can @intCast. Ideally you would be able to specify the type of the range, but that was explicitly denied
IMO, embedding initialization in loops leads to bugs esp w/multiple loops and memory allocation…you have an init, need a de-init too! zig gets it right 100%.
for (var outer = alloc_outer_struct() ... ) {
for (var inner = alloc_inner_struct() ... ) { //ahh...C...ty for this gift.
...
}
}
There’s a non-trivial chance that we’ll get that with integer ranges. There’s been active interest from the compiler team in implementing ranges, I consider it one of the most high-impact additions to the language which are on the table.