Blocks
Blocks are used to limit the scope of variable declarations:
test.zig
test "access variable after block scope" {
{
var x: i32 = 1;
_ = x;
}
x += 1;
}
Shell
$ zig test test.zig
docgen_tmp/test.zig:6:5: error: use of undeclared identifier 'x'
x += 1;
^
Blocks are expressions. When labeled, break
can be used to return a value from the block:
test_labeled_break.zig
const std = @import("std");
const expect = std.testing.expect;
test "labeled break from labeled block expression" {
var y: i32 = 123;
const x = blk: {
y += 1;
break :blk y;
};
try expect(x == 124);
try expect(y == 124);
}
Shell
$ zig test test_labeled_break.zig
1/1 test.labeled break from labeled block expression... OK
All 1 tests passed.
Here, blk
can be any name.
See also:
Shadowing
Identifiers are never allowed to “hide” other identifiers by using the same name:
test.zig
const pi = 3.14;
test "inside test block" {
// Let's even go inside another block
{
var pi: i32 = 1234;
}
}
Shell
$ zig test test.zig
docgen_tmp/test.zig:6:13: error: local variable shadows declaration of 'pi'
var pi: i32 = 1234;
^~
docgen_tmp/test.zig:1:1: note: declared here
const pi = 3.14;
^~~~~~~~~~~~~~~
Because of this, when you read Zig code you can always rely on an identifier to consistently mean the same thing within the scope it is defined. Note that you can, however, use the same name if the scopes are separate:
test_scopes.zig
test "separate scopes" {
{
const pi = 3.14;
_ = pi;
}
{
var pi: bool = true;
_ = pi;
}
}
Shell
$ zig test test_scopes.zig
1/1 test.separate scopes... OK
All 1 tests passed.
Empty Blocks
An empty block is equivalent to void{}
:
empty_block.zig
const std = @import("std");
const expect = std.testing.expect;
test {
const a = {};
const b = void{};
try expect(@TypeOf(a) == void);
try expect(@TypeOf(b) == void);
try expect(a == b);
}
Shell
$ zig test empty_block.zig
1/1 test_0... OK
All 1 tests passed.