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. _ = x;
  5. }
  6. x += 1;
  7. }

Shell

  1. $ zig test test.zig
  2. docgen_tmp/test.zig:6: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_labeled_break.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. try expect(x == 124);
  10. try expect(y == 124);
  11. }

Shell

  1. $ zig test test_labeled_break.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

Identifiers are never allowed to “hide” other identifiers 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. }

Shell

  1. $ zig test test.zig
  2. docgen_tmp/test.zig:6:13: error: local shadows declaration of 'pi'
  3. var pi: i32 = 1234;
  4. ^
  5. docgen_tmp/test.zig:1:1: note: declared here
  6. const pi = 3.14;
  7. ^

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

  1. test "separate scopes" {
  2. {
  3. const pi = 3.14;
  4. _ = pi;
  5. }
  6. {
  7. var pi: bool = true;
  8. _ = pi;
  9. }
  10. }

Shell

  1. $ zig test test_scopes.zig
  2. 1/1 test "separate scopes"... OK
  3. All 1 tests passed.