Variables

A variable is a unit of Memory storage.

It is generally preferable to use const rather than var when declaring a variable. This causes less work for both humans and computers to do when reading code, and creates more optimization opportunities.

Identifiers

Variable identifiers are never allowed to shadow identifiers from an outer scope.

Identifiers must start with an alphabetic character or underscore and may be followed by any number of alphanumeric characters or underscores. They must not overlap with any keywords. See Keyword Reference.

If a name that does not fit these requirements is needed, such as for linking with external libraries, the @"" syntax may be used.

identifiers.zig

  1. const @"identifier with spaces in it" = 0xff;
  2. const @"1SmallStep4Man" = 112358;
  3. const c = @import("std").c;
  4. pub extern "c" fn @"error"() void;
  5. pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int;
  6. const Color = enum {
  7. red,
  8. @"really red",
  9. };
  10. const color: Color = .@"really red";

Container Level Variables

Container level variables have static lifetime and are order-independent and lazily analyzed. The initialization value of container level variables is implicitly comptime. If a container level variable is const then its value is comptime-known, otherwise it is runtime-known.

test_container_level_variables.zig

  1. var y: i32 = add(10, x);
  2. const x: i32 = add(12, 34);
  3. test "container level variables" {
  4. try expect(x == 46);
  5. try expect(y == 56);
  6. }
  7. fn add(a: i32, b: i32) i32 {
  8. return a + b;
  9. }
  10. const std = @import("std");
  11. const expect = std.testing.expect;

Shell

  1. $ zig test test_container_level_variables.zig
  2. 1/1 test.container level variables... OK
  3. All 1 tests passed.

Container level variables may be declared inside a struct, union, enum, or opaque:

test_namespaced_container_level_variable.zig

  1. const std = @import("std");
  2. const expect = std.testing.expect;
  3. test "namespaced container level variable" {
  4. try expect(foo() == 1235);
  5. try expect(foo() == 1236);
  6. }
  7. const S = struct {
  8. var x: i32 = 1234;
  9. };
  10. fn foo() i32 {
  11. S.x += 1;
  12. return S.x;
  13. }

Shell

  1. $ zig test test_namespaced_container_level_variable.zig
  2. 1/1 test.namespaced container level variable... OK
  3. All 1 tests passed.

Static Local Variables

It is also possible to have local variables with static lifetime by using containers inside functions.

test_static_local_variable.zig

  1. const std = @import("std");
  2. const expect = std.testing.expect;
  3. test "static local variable" {
  4. try expect(foo() == 1235);
  5. try expect(foo() == 1236);
  6. }
  7. fn foo() i32 {
  8. const S = struct {
  9. var x: i32 = 1234;
  10. };
  11. S.x += 1;
  12. return S.x;
  13. }

Shell

  1. $ zig test test_static_local_variable.zig
  2. 1/1 test.static local variable... OK
  3. All 1 tests passed.

The extern keyword or @extern builtin function can be used to link against a variable that is exported from another object. The export keyword or @export builtin function can be used to make a variable available to other objects at link time. In both cases, the type of the variable must be C ABI compatible.

See also:

Thread Local Variables

A variable may be specified to be a thread-local variable using the threadlocal keyword, which makes each thread work with a separate instance of the variable:

test_thread_local_variables.zig

  1. const std = @import("std");
  2. const assert = std.debug.assert;
  3. threadlocal var x: i32 = 1234;
  4. test "thread local storage" {
  5. const thread1 = try std.Thread.spawn(.{}, testTls, .{});
  6. const thread2 = try std.Thread.spawn(.{}, testTls, .{});
  7. testTls();
  8. thread1.join();
  9. thread2.join();
  10. }
  11. fn testTls() void {
  12. assert(x == 1234);
  13. x += 1;
  14. assert(x == 1235);
  15. }

Shell

  1. $ zig test test_thread_local_variables.zig
  2. 1/1 test.thread local storage... OK
  3. All 1 tests passed.

For Single Threaded Builds, all thread local variables are treated as regular Container Level Variables.

Thread local variables may not be const.

Local Variables

Local variables occur inside Functions, comptime blocks, and @cImport blocks.

When a local variable is const, it means that after initialization, the variable’s value will not change. If the initialization value of a const variable is comptime-known, then the variable is also comptime-known.

A local variable may be qualified with the comptime keyword. This causes the variable’s value to be comptime-known, and all loads and stores of the variable to happen during semantic analysis of the program, rather than at runtime. All variables declared in a comptime expression are implicitly comptime variables.

test_comptime_variables.zig

  1. const std = @import("std");
  2. const expect = std.testing.expect;
  3. test "comptime vars" {
  4. var x: i32 = 1;
  5. comptime var y: i32 = 1;
  6. x += 1;
  7. y += 1;
  8. try expect(x == 2);
  9. try expect(y == 2);
  10. if (y != 2) {
  11. // This compile error never triggers because y is a comptime variable,
  12. // and so `y != 2` is a comptime value, and this if is statically evaluated.
  13. @compileError("wrong y value");
  14. }
  15. }

Shell

  1. $ zig test test_comptime_variables.zig
  2. 1/1 test.comptime vars... OK
  3. All 1 tests passed.