Variables

A variable is a unit of Memory storage.

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

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.

Global Variables

Global variables are considered to be a top level declaration, which means that they are order-independent and lazily analyzed. The initialization value of global variables is implicitly comptime. If a global variable is const then its value is comptime-known, otherwise it is runtime-known.

global_variables.zig

  1. var y: i32 = add(10, x);
  2. const x: i32 = add(12, 34);
  3. test "global 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;
  1. $ zig test global_variables.zig
  2. Test [1/1] test "global variables"...
  3. All 1 tests passed.

Global variables may be declared inside a struct, union, or enum:

namespaced_global.zig

  1. const std = @import("std");
  2. const expect = std.testing.expect;
  3. test "namespaced global 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. }
  1. $ zig test namespaced_global.zig
  2. Test [1/1] test "namespaced global variable"...
  3. All 1 tests passed.

The extern keyword 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:

tls.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.wait();
  9. thread2.wait();
  10. }
  11. fn testTls(context: void) void {
  12. assert(x == 1234);
  13. x += 1;
  14. assert(x == 1235);
  15. }
  1. $ zig test tls.zig
  2. Test [1/1] test "thread local storage"...
  3. All 1 tests passed.

For Single Threaded Builds, all thread local variables are treated as Global 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.

comptime_vars.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. }
  1. $ zig test comptime_vars.zig
  2. Test [1/1] test "comptime vars"...
  3. All 1 tests passed.