Labeled blocks
A labeled block is not a decision structure in itself: it’s a block of code from which we can return values, as if it were an expression, allowing us to assign results to variables or constants.
const (or var) identifier = label_name: {
< code inside the block >
break :label_name returned_value;
} ;
Block labels are defined using a name (identifier) followed by a colon, with no space in between. The name must follow the usual rules for identifiers in Zig. It’s recommended, though not required, that the label name be meaningful, as this makes future code reviews easier to read.
When returning a value from a labeled block, we use the keyword break, followed by a space, a colon, and the name of the previously defined label.
As an example of usage, let’s return a simple response from a labeled block:
labelled_response.zig |
|
const std = @import("std"); const print = std.debug.print; pub fn main() void { const n_x = important_label: { break :important_label 42; // return 42 which gets assigned to n_x }; print("Answer to the ultimate question of life, the universe, and everything: {d}\n", .{n_x}); } |
|
$ zig run labelled_response.zig |
|
Answer to the ultimate question of life, the universe, and everything: 42 |
Just like with value-returning if/else expressions, here we assign a result to a constant. Since n_x is a constant, it’s immutable, which helps reduce potential bugs.
The logic inside a labeled block can be much more complex than in the example. Obviously, it’s always best to keep it as clear as possible by breaking the code into smaller, more understandable parts to improve readability.
An empty block returns void:
empty_block.zig |
|
const std = @import("std"); const print = std.debug.print; pub fn main() void { const n_x = {}; print("Empty: {} \n", .{n_x}); } |
|
$ zig run empty_block.zig |
|
Empty: void |
As long as they’re not nested, labels can have the same name.