inline for unrolls a loop. You can’t continue an unrolled loop, but you can break from one of its blocks.
var number = 1;
do: {
if (value == number) {
break :do;
}
std.debug.print("{d}\n", .{number});
}
number = 2;
do: {
if (value == number) {
break :do;
}
std.debug.print("{d}\n", .{number});
}
number = 3; // etc
Compared with:
var number = 1;
if (value == number) continue;
// there's nothing to continue
This is consistent with the documentation for inline for:
For loops can be inlined. This causes the loop to be unrolled, which allows the code to do some things which only work at compile time, such as use types as first class values.
I would rather the compiler be consistent about this semantics, rather than try to translate constructs which don’t work in an unrolled loop into constructs which do.
fn runtimeBreak(value: i32) void {
var number: i32 = 1;
inline_for: {
if (value == number) break :inline_for;
std.debug.print("{d}\n", .{number});
number = 2;
if (value == number) break :inline_for;
std.debug.print("{d}\n", .{number});
number = 3;
if (value == number) break :inline_for;
std.debug.print("{d}\n", .{number});
number = 4;
if (value == number) break :inline_for;
std.debug.print("{d}\n", .{number});
number = 5;
if (value == number) break :inline_for;
std.debug.print("{d}\n", .{number});
}
}
test "runtime break" {
runtimeBreak(4);
}
Granted, this is a little pinch of magic.
I’d call it an affordance: the compiler could require that we put a label on the inline for block, and use the label, but instead it’s kind enough to let us use the break statement directly.
But this is different from continue, which only works in a loop. Which isn’t what an inline for compiles to.
I consider the compiler inferring a label to be different from it translating one control-flow primitive into another, but YMMV.
Hm, I honestly think it’d be reasonable for continue to work for inline loops. It’d be pretty damn easy to make work in the compiler, too. I’d be happy for someone to open a proposal.
My feelings on the subject aren’t strong ones, but I’m inclined to think that the reminder that inline for isn’t a loop, that it’s unrolled, might be worth preserving.
On the other hand, it does have the “wait why doesn’t this work?” quality, so there’s that. A proposal would be the right place to hash it all out.
Just hit this when implementing a deserializer for message pack and I wanted the deserializer to “retry” for every field in a union until one succeeded:
There’s a small “hack” you can use in these situations as well. You simply break from the block instead of continuing the loop. This will jump you straight to the next unrolled block of the inline for loop at runtime and works effectively as a continue.