The Semicolon Syntax Riddle

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.

1 Like

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.

6 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

4 Likes

might be related Allow any statement in loop bodies · Issue #5731 · ziglang/zig · GitHub

1 Like

No!

Thanks all. Study to do :slight_smile:

#1755 aims to reduce this kind of surprise.

3 Likes