Range: a simple little numeric range iterator

With one additional line of code, you get:

1. Control the type of iterated numeric values.
``````var r = Range(i8){ .from = -2, .to = 2 };
while (r.next()) |i| { ...
``````
1. Ascending or descending iteration.
``````var r = Range(i8){ .from = 2, .to = -2 };
while (r.next()) |i| { ...
``````
1. Control the iteration step.
``````var r = Range(i8){ .from = -2, .to = 2, .step = 2 };
while (r.next()) |i| { ...
``````
1. Range over the values of a type.
``````var r = Range(u4){ .inclusive = true };
while (r.next()) |i| { ...
``````
13 Likes

This looks great @dude_the_builder . I am trying to look at the source, but codeberg.org is excruciatingly slow for me, for some reason.

1 Like

Yeah, sometimes itâ€™s fast, sometimes itâ€™s really slow, sometimes itâ€™s plain unavailable. I guess itâ€™s the price to pay for not hosting on a tech giantâ€™s infrastructure.

Cool little utility!

I have a suggestion: â€śunboundedâ€ť ranges donâ€™t exist, and the way you use null and saturating add (if Iâ€™m reading the code correctly) would produce an infinite loop of the maximum value that the numeric type can represent.

You could use the type-maximum for `.to` when itâ€™s not specified, and return null when `.i.?` is `==` to `.to`. Since `.i` starts as `null`, this should make e.g. `0..255` all representable for a `u8`, since you return the incremented value, therefore the final returned value of `.i.?` would be 255, then it would be checked, found to be equal, and the iterator would return `null`.

This also wouldnâ€™t need to use saturating arithmetic.

Commenting on code one hasnâ€™t run is a bad habit, so if I missed something about the implementation and all of this is wrong, sorry about that! I also want to note that this is an incomplete observation, which only applies to ranges which increment.

2 Likes

The upper bound is infinity. lol But seriously, Iâ€™m kind of on the fence with the unbounded option. The repeating maximum value for the type is exactly the behavior that occurs and Iâ€™m hard-pressed to think of a useful use-case for that.

I think I need it because of `.step`, which can cause the last increment / decrement to go past the max / min for the type, right?

Either way, those are some great observations, thanks for taking the time!

1 Like

Ok, so thanks to @mnemnionâ€™s observations, I replaced the unbounded option with type min / max default values for `from` and `to`. Also added an `inclusive` field to be able to easily iterate over a typeâ€™s range of values:

``````var r = Range(u8){ .inclusive = true };
while (r.next()) |i| { // 0-255
``````
2 Likes

Very neat, I like how simple + readable the implementation is for it too, also no idea Zig had saturating arithmetic operators until now, this seems like a really great use for them!

2 Likes

Okay, now thatâ€™s really cool. I like that you can span the entire value set for smaller integer types.

1 Like

If you wanted to go full Dijkstra on this, you could support inclusive / exclusive on both ends, with four total combinations. But I am not aware exclusive on the starting value would ever be usefulâ€¦ Great job!

1 Like