This compiles (if we declare std).
for (0..12)|i| for (i + 1..12)|j| for (j + 1..12)|k|
std.debug.print("{},{},{}\n", .{i,j,k});
This however not. It requires an extra ; after the closing brace.
for (0..12)|i| for (i + 1..12)|j| for (j + 1..12)|k| {
std.debug.print("{},{},{}\n", .{i,j,k});
} // ; needed
If we use braces for every loop (of course) not.
for (0..12)|i| {
for (i + 1..12)|j| {
for (j + 1..12)|k| {
std.debug.print("{},{},{}\n", .{i,j,k});
}
}
}
What is the idea behind it?
I think it is syntactically very strange.
2 Likes
Because the inner loops become expressions which need to be terminated with ;.
an easier to understand example would be
const foo = for (0..1) |n| {
//...
} else ...; // did ya know you could have an else on a loop :3
everything after the = becomes an expression.
10 Likes
this is due to how for statements are defined in the zig grammar. There’s actually 2 variants of for, ForStatement and ForExpr, one used when it’s present as a regular statement inside a statement block, the other one where an expression is expected.
The first for loop with the (0..12)|i| parameters is a ForStatement, which can have the form for (...)|...| {} or for (...)|...| <expression>; (ignoring for ... else ... here for simplicity)
The inner for loops are ForExpr in the cases where their parent for loop is the <expression> version, even if these nested ForExpr instances have a {} body, so you still need the terminating semicolon on the top-level brace-less ForStatement
9 Likes
#1755 aims to reduce this kind of surprise.
5 Likes
Quite confusing actually…
Is there a situation thinkable in which }; is really really needed?
In my simple mind a closing brace is closing whatever there is inside.
const x: i32 = switch (x) {...} // why put a ; here?
I think in C# it is about the same.
Yes, it makes perfect sense to me when I
const foo: Bar = init: {
// ...
break :init bar;
}; // here I really expect the ; - I'm making an assignment
I meant “needed”. Not “making sense”. It makes sense to me in this case too
const x: i32 = switch(x) {...}
- 1; // ambiguous
With optional semicolons the compiler could either assume a semicolon is present, and throw error: value of type 'comptime_int' ignored for the - 1 on the line after, or assume a semicolon is not present, and compile without issues. With a required semicolon there’s no ambiguity, the - 1; is part of the initializer expression of x.
1 Like
This is a few years old but I think the language is unchanged in this regard: