Integer range based loops

Back to basics today. I have seen the Zig style for integer range loops. But I find it cumbersome that ‘i’ pollutes the namespace within that block. i.e. I can only do that once, and then have to use a different var, or set i to 0. And not to mention 3 lines of code…

var i: usize = 0;
while (i < 10) {
    i += 1;
}

So for const range based loops I find myself doing:

const range = [_]u8{0} ** pointCount;

fn f() void {
    for (range) |_, i| {
    }
}

or even this:

for ([_]u8{0} ** itemCount) |_, i| {
}

maybe u1 or bool is even better…

And of course this hack only works for fixed sized loops (const itemCount).

So my question: is there such a hack for using a ‘var itemCount’? i.e. variable ranges.

I was even considering a global array holding values that match each index, and then taking slices to pull out various range sets.

const indxs = [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

Did I miss something more idiomatic rather than idiotic :slight_smile:

Or maybe something like Kotlin’s ‘Ranges’ functionality.

for (i in 1 until 10) {       // i in [1, 10), 10 is excluded
}

[edit] So I just tried the indxs idea, I must be unlucky today, the first value I picked seems to be some kind of compiler limit. So doing this:

var range2 = init: {
    var initial_value: [1000]u16 = undefined;
    for (initial_value) |*v, i| {
        v.* = i;
    }
    break :init initial_value;
};

and:

for (range2) |_, i| {
}

results in compile error:

error: evaluation exceeded 1000 backwards branches

using Zig 0.8.1 on macos 10.12.4

Anyway, this is not a problem for me, just playing around with these loop ideas, but thought I should feed that back in case I stumbled over some unexpected behavior.

I’m on 0.9 so I don’t know if this will work the same in 0.8.1:

const std = @import("std");

fn makeRange(comptime T: type, comptime n: usize) [n]T {
    var range: [n]T = undefined;
    for (range) |*v, i| v.* = i;
    return range;
}

pub fn main() void {
    for (makeRange(usize, 10)) |i| std.debug.print("{}\n", .{i});
}

Here you take advantage of Zig’s comptime evaluation to prepare your range array with the size and type you need.

As a separate observation, note that in the normal while case, you can reduce it by specifying the continuation expression as part of the while line:

var i: usize = 0;
while (i < 10) : (i += 1) {}

Given that in Zig you can have independent blocks of code as expressions, you could do quite a lot in that continuation expression, for example (taken from the language reference):

var i: usize = 1;
var j: usize = 1;
while (i * j < 2000) : ({ i *= 2; j *= 3; }) {
    const my_ij = i * j;
    try expect(my_ij < 2000);
}

I went with my ‘global’ array hack (currently limited to 999) and small method to pull out runtime sized slices:

var staticRange = init: {
    var initial_value: [999]u16 = undefined;
    for (initial_value) |*v, i| {
        v.* = i;
    }
    break :init initial_value;
};

pub fn rangeFromUntil(from: u16, until: u16) []u16 {
    return staticRange[from..until];
}

pub fn range(until: u16) []u16 {
    return staticRange[0..until];
}

Otherwise I would end up with lots of ‘garbage’ arrays in the binary? And this then also avoids the temporary loop counter namespace pollution (which the shorter while style that you mentioned also has). Feels like a hack, especially since I noticed numeric ranges on the switch statements today which would be close to doing something like:

for (0...9) |v, i| {
}

not that I like the ambiguity on such things i.e. is the 9 included or not! which I guess is why Kotlin went with a more verbose syntax. Anyway thanks for the feedback.