This is a brain dump of what I find annoying/unintuitive about break
const x = blk: while (true) {
break :blk 0;
};
compiles
const x = blk: {
break :blk 0;
};
compiles
const x = while (true) {
break 0;
};
compiles
const x = {
break 0;
};
doesn’t compile (break is missing label)
const x = if (true) blk: {
break :blk 0;
} else 1;
compiles
const x = blk: if (true) {
break :blk 0;
} else 1;
doesn’t compile (labeled if is not a valid construct)
const x = if (true) {
break 0;
} else 1;
doesn’t compile (break is missing label)
const x: u32 = while (true) {
break 0;
} else 1;
compiles
const x: u32 = while (true) {
break 0;
} else {
break 1;
};
doesn’t compile (else is missing block label to return to)
const x: u32 = while (true) {
break 0;
} else blk: {
break :blk 1;
};
compiles
const x: u32 = while (true) blk: {
break :blk 0;
} else blk: {
break :blk 1;
};
doesn’t compile (value returned from block is ignored)
const x: u32 = blk: while (true) {
break :blk 0;
} else blk: {
break :blk 1;
};
doesn’t compile (redefinition of blk)
const x: u32 = blk: while (true) {
break :blk 0;
} else {
break :blk 1;
};
compiles
It would feel more intuitive to me if break was something like
break returns a value from the nearest block or the block with the optionally provided label.
with while being
A while loop is used to repeatedly execute an expression until some condition is no longer true or the expression returns a non-void value
That doesn’t work though because
while (true) {
break;
}
returns a void value, which would loop forever.
In what context does it make sense to use break outside of a block?
sw: switch (thing) {
0 => break :sw,
// ...
}
is the same as
sw: switch (thing) {
0 => {},
// ...
}
and
while (true) break;
for (0..1) |_| break;
are both seemingly useless.
I just realized that you can break a value from a labeled switch
const x = sw: switch (0) {
0 => break :sw 1,
else => unreachable,
};
I feel like there is some satisfying unification of break revolving around blocks that doesn’t fall apart immediately but it’s not coming to me. I would also be a-okay with the removal of unlabeled break.
off-topic: labeled if is maybe useful? Here’s a potential use-case from the zig compiler (resolveReferencesInner in src/Zcu.zig)
while (true) {
if (type_idx < types.count()) {
// ...
continue;
}
if (unit_idx < units.count()) {
// ...
continue;
}
break;
}
i: if (type_idx < types.count()) {
// ...
continue :i;
} else if (unit_idx < units.count()) {
// ...
continue :i;
}
Maybe this generates better code? Probably not since its just a jmp to the beginning of the loop in both cases. I at least think it’s easier to read the intention. This would also allow for:
const x = blk: if (true) {
break :blk 0;
} else {
break :blk 1;
};
instead of
const x = if (true) blk: {
break :blk 0;
} else blk: {
break :blk 1;
};
or
const x = blk: {
if (true) {
break :blk 0;
} else {
break :blk 1;
}
};