Hi.
I’m a bit confused with compiler error message.
Here is simple prog:
// count up
const std = @import("std");
pub fn main() void {
const n : i32 = 3;
for (0 .. n) |k| {
std.debug.print("k = {}\n", .{k});
}
}
Sure it’s working as expected:
$ ./for
k = 0
k = 1
k = 2
Now I want to count down:
// count down
const std = @import("std");
pub fn main() void {
const n : i32 = 3;
for (n .. 0) |k| {
std.debug.print("k = {}\n", .{k});
}
}
Ooops… I get this somewhat strange message:
$ /opt/zig-0.11/zig build-exe for_.zig
for_.zig:5:12: error: overflow of integer type 'usize' with value '-3'
for (n .. 0) |k| {
~~^~~~
So three questions:
- why
usize
? I used i32
for n
- why
-3
?!? (it is always equal to -n
)
- is there a way to count down using
for
with a range?
for
-loops only support counting up. For counting down you have to use a while loop:
const n i32 = 3;
var i = n-1;
while(start >= 0) : (i -= 1) {
...
}
or you can use a for-loop that counts up and then reverse the loop parameter:
for(0..n) |reverse_i| {
const i = n - 1 - @as(i32, @intCast());
}
for
-loops only support usize
for the index type. So it tries to cast your i32
to a usize
.
for
-loops first calculate the length of the loop, in your case end - start = 0 - 3 = -3
that’s where the -3 comes from.
This error message is pretty bad at the moment. Some bad error message should be expected until zig matures. Improving this particular error message for example is scheduled for zig 1.1.0: Better error message when `start` is bigger than the `end` in `for(start..end)` · Issue #14701 · ziglang/zig · GitHub
2 Likes
@IntegratedQuantum
Ok, thanks, I got It.
Will mark your answer as solution.
That’s a pity
It would be really great if they’d also be able to count down.
1 Like
It turns out that at least for this simple example, Zig can coerce the type of k so a simple subtraction does the trick:
const std = @import("std");
pub fn main() void {
const n: i32 = 3;
for (0..n) |k| {
std.debug.print("k = {}\n", .{n - k});
}
}
1 Like
yes, it’s good, no problem at all to countdown somehow,
but I wanted to do that with ‘for(5 .. 0)
’
If I was a… compiler :), I’d probably did the following
- number of iterations is negative? ok
** let’s negate this number, now it’s positive
** use -1
as increment
- but there is also a question of symmetry… in
for(0 .. n)
the interval is open at it’s right end… probably, in for(5 .. 0)
left end of the interval should be open.
for
loops are for dead-simple linear, forward scanning through a sequence. all more complicated kinds of looping is handled by while
, or at least this seems to be the current philosophy.
1 Like
With comptime, you can be the compiler. :^)
const std = @import("std");
fn getAbsLen(comptime from: isize, comptime to: isize) usize {
const diff: f64 = @floatFromInt(to - from);
return @fabs(diff) + 1;
}
fn range(comptime from: isize, comptime to: isize) [getAbsLen(from, to)]isize {
var array: [getAbsLen(from, to)]isize = undefined;
var item: isize = from;
for (&array) |*i| {
i.* = item;
item = if (from < to) item + 1 else item - 1;
}
return array;
}
pub fn main() void {
for (range(3, -3)) |k| {
std.debug.print("k = {}\n", .{k});
}
}
Note this code doesn’t check for overflow, that’s left as an exercise for the reader. lol
2 Likes
But only for comptime known range ends, right?
fn range(comptime from: isize, comptime to: isize) [getAbsLen(from, to)]isize {
Wow, that’s cool! Thanks for this nice example!
Edit: I meant that I have not cognized all the power of comptime
yet and hence calling a function in function return type was a sort of revelation for me 
Yes, and they are semantically more like foreach
in other langs rather than for
in C/Pascal and alike. And this is a common point of confusion when a learner see this for the first time.
1 Like
Yeah, since we’re calculating the length of an array from the range bounds, they have to be comptime known.
I would like to see something like
“for loops can not count down”
3 Likes
I know this is an old thread but i was dealing with this specific problem recently, so i just want to add my opinion. Basically i came to the understsnding that while loops are generally more flexible. You can also do nested while loops to establish different conditions. When you break out of the while loop you will still be at the same position left you left off at. However this is not possible with a for loop. Since the index of iteration is constant, when you break out you will still be at the same index you were when you went into the nested loop. So i would say if you cannot do something with a for loop and you really want to use a for loop, then you might look for an opportunity to reorganize your data, to get it in a format that would conform to the strict requirements for using a for loop. i.e. you can build your cache using a while loop, then when you access that cache you can use the for loop to access your optimized data