blocks

Blocks are used to limit the scope of variable declarations:

test.zig

  1. test "access variable after block scope" {
  2. {
  3. var x: i32 = 1;
  4. }
  5. x += 1;
  6. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:5:5: error: use of undeclared identifier 'x'
  3. x += 1;
  4. ^

Blocks are expressions. When labeled, break can be used to return a value from the block:

test.zig

  1. const std = @import("std");
  2. const expect = std.testing.expect;
  3. test "labeled break from labeled block expression" {
  4. var y: i32 = 123;
  5. const x = blk: {
  6. y += 1;
  7. break :blk y;
  8. };
  9. expect(x == 124);
  10. expect(y == 124);
  11. }
  1. $ zig test test.zig
  2. 1/1 test "labeled break from labeled block expression"... OK
  3. All 1 tests passed.

Here, blk can be any name.

See also:

Shadowing

It is never allowed for an identifier to “hide” another one by using the same name:

test.zig

  1. const pi = 3.14;
  2. test "inside test block" {
  3. // Let's even go inside another block
  4. {
  5. var pi: i32 = 1234;
  6. }
  7. }
  1. $ zig test test.zig
  2. ./docgen_tmp/test.zig:6:9: error: redefinition of 'pi'
  3. var pi: i32 = 1234;
  4. ^
  5. ./docgen_tmp/test.zig:1:1: note: previous definition is here
  6. const pi = 3.14;
  7. ^

Because of this, when you read Zig code you can rely on an identifier always meaning the same thing, within the scope it is defined. Note that you can, however use the same name if the scopes are separate:

test.zig

  1. test "separate scopes" {
  2. {
  3. const pi = 3.14;
  4. }
  5. {
  6. var pi: bool = true;
  7. }
  8. }
  1. $ zig test test.zig
  2. 1/1 test "separate scopes"... OK
  3. All 1 tests passed.