Skip to content

Variables and Constants

A program stores values so it can use them later. In Zig, you store values with two main keywords:

A program stores values so it can use them later. In Zig, you store values with two main keywords:

const
var

Use const when the value will not change.

Use var when the value can change.

This is one of the first important habits in Zig: prefer const first. Only use var when you truly need to modify the value.

Constants

A constant is a named value that cannot be changed after it is created.

const age = 30;

This creates a name called age and gives it the value 30.

You can use it later:

const std = @import("std");

pub fn main() void {
    const age = 30;

    std.debug.print("Age: {}\n", .{age});
}

Output:

Age: 30

The value of age cannot be changed:

const age = 30;
age = 31; // error

Zig rejects this because age was declared with const.

This is useful. It prevents accidental changes.

Variables

A variable is a named value that can be changed.

var count = 0;

Now count can be updated:

const std = @import("std");

pub fn main() void {
    var count = 0;

    count = count + 1;
    count = count + 1;

    std.debug.print("Count: {}\n", .{count});
}

Output:

Count: 2

Use var when the value represents something that changes over time: a counter, an index, a temporary buffer, a running total, or a mutable state.

Zig Wants You to Be Honest

Zig checks whether a variable really needs to be mutable.

This code is suspicious:

var x = 10;
std.debug.print("{}\n", .{x});

The value x never changes. Zig may tell you to use const instead.

The better version is:

const x = 10;
std.debug.print("{}\n", .{x});

This makes the code clearer. A reader can immediately know that x will not change.

Type Inference

Zig can often figure out the type from the value.

const x = 10;
const name = "Zig";
const enabled = true;

Here, Zig infers the types:

const x: comptime_int = 10;
const name: *const [3:0]u8 = "Zig";
const enabled: bool = true;

You do not need to write those types manually at the start. For beginners, it is usually better to let Zig infer simple local values.

But you can also write the type yourself:

const age: u8 = 30;
var count: i32 = 0;
const pi: f64 = 3.14159;

The syntax is:

const name: Type = value;
var name: Type = value;

Read this as:

create name with type Type and value value

Basic Example

const std = @import("std");

pub fn main() void {
    const language = "Zig";
    const year: u16 = 2026;

    var score: i32 = 0;
    score = score + 10;
    score = score + 5;

    std.debug.print("{s} in {}\n", .{ language, year });
    std.debug.print("Score: {}\n", .{score});
}

Output:

Zig in 2026
Score: 15

In this example:

const language = "Zig";
const year: u16 = 2026;

These do not change, so they are constants.

var score: i32 = 0;
score = score + 10;
score = score + 5;

This changes, so it is a variable.

Assignment

Assignment means giving a new value to an existing variable.

var n = 5;
n = 8;

After this code runs, n contains 8.

You can assign only to something declared with var.

const n = 5;
n = 8; // error

A constant has one value. Once created, it cannot be reassigned.

Declaration vs Assignment

These two ideas are different.

Declaration creates the name:

var count: i32 = 0;

Assignment changes the value later:

count = 1;

You can combine declaration and initial value in one line:

const x = 10;

This is the common form.

Mutable Does Not Mean Careless

var should not be used just because it feels convenient.

This is weak code:

var max_connections = 100;

If max_connections never changes, it should be:

const max_connections = 100;

That small choice matters. In a large program, every mutable value is something the reader must track. Constants reduce the amount of state in the program.

Good Zig code uses const heavily.

Shadowing

Zig does not allow you to declare a local variable with the same name in the same scope.

const x = 10;
const x = 20; // error

This prevents confusion. If a name already means one thing, Zig does not let you reuse it in the same block.

You can create a new scope with braces:

const std = @import("std");

pub fn main() void {
    const x = 10;

    {
        const y = 20;
        std.debug.print("{} {}\n", .{ x, y });
    }
}

The name y exists only inside the inner block.

Uninitialized Variables

Sometimes you may want to declare a variable before giving it a real value.

Zig allows this with undefined:

var x: i32 = undefined;
x = 42;

This means: create x, but its starting value is not meaningful.

Be careful. Reading an undefined value is a bug.

var x: i32 = undefined;
std.debug.print("{}\n", .{x}); // bug

Use undefined only when you are sure the value will be assigned before it is read.

For beginners, prefer immediate initialization:

const x: i32 = 42;

Constants Can Hold Complex Values

A constant does not mean the value must be small.

You can use const for arrays, structs, imports, functions, and many other things.

const numbers = [_]i32{ 1, 2, 3, 4 };

This creates a constant array.

const std = @import("std");

This creates a constant binding to the standard library import.

In Zig, const is not only for numbers. It means the name cannot be reassigned.

The Main Rule

Start with const.

Change it to var only when the value must change.

const name = "Ada"; // good: does not change

var counter: usize = 0; // good: changes over time
counter += 1;

This habit makes Zig code simpler, safer, and easier to read.