Module variables

A module variable is one storage location for the whole program, declared at module level with var. Every function that can name it reads and writes the same place. A thread-local variable, declared with thread var, is the same except that every thread has its own copy.

module-variable = ["pub"] ["thread"] "var" Name ":" type "=" expression ";"
var Requests: uint64 = 0;
pub var Verbosity: int32 = 1;

func Record() {
    Requests += 1;
}

The type is always written, because other functions and packages agree on the variable's layout, and an initial value is required. let has no module-level form: a value that never changes is a constant.

The initial value

The initial value follows the rules of a constant's initializer: the compiler computes it from literals, operators, casts, constants, and struct, array and tuple values built from them. A call or another variable is rejected with the same messages a constant gets. The program starts with the variable holding exactly those bytes. A value whose bytes are all zero is placed in zero-fill storage and takes no space in the executable; any other value is placed in initialized data.

Visibility and paths

pub var publishes a module variable like any other declaration, so another package can import it, read it and assign to it. A variable in another module is reached through its path:

Settings::Level = 5;

Module variables are named in PascalCase, like other module-level declarations. Inside a function body, var declares an ordinary local, and pub var there is an error.

Addresses

@Name is a writable *var T to the variable's storage, aligned as alignof(T) reports. Every @Name, in every package, names the same address. That is what makes a module variable usable with the atomic operations, which take the address of the value they act on.

What a module variable may hold

Module storage lives until the program exits and is never destroyed, so a module variable cannot have a type that needs destruction. It cannot hold a slice either, since a slice borrows storage the variable does not own.

Nothing moves out of a module variable: every function shares it, so it must always hold a value.

error: cannot move out of module variable 'Requests'
  note: a module variable is shared by every function and must always hold a value
  help: copy the value, or assign a new value to the variable after reading it

Thread-local variables

thread var gives every thread its own copy of the variable. Each thread's copy starts as the initial value, whether the thread is the main one or one started later, and keeps its address for the thread's life, so @Name on two threads gives two addresses.

thread var Depth: int32 = 0;

func Enter() -> int32 {
    Depth += 1;
    return Depth;
}

Everything else follows the rules above: the type and a compile-time initial value are written out, pub thread var publishes the variable, a variable in another module is reached through its path, a type that needs destruction or a slice is rejected, and nothing moves out. thread is not a keyword: it means this only before a module-level var, and stays an ordinary name everywhere else.

A thread-local variable costs one or two instructions more than a module variable to reach:

TargetHow a thread finds its copy
Linux, FreeBSDat a fixed offset from the thread pointer, set up by the dynamic loader; the program links the C library
Windowsthrough the image's TLS directory, from which the loader gives each thread a copy of the template
macOSthrough a descriptor that dyld binds; reading the variable calls through it

A shared library or a static library with thread-local variables is rejected for now.

See also

  • Constants — the initializer rules a module variable's initial value follows
  • Atomics — reading and writing shared memory from several threads
  • Visibility — what pub publishes