An array literal is the syntax you use to write array values directly in source code.
An array literal is the syntax you use to write array values directly in source code.
You already saw this form:
const numbers = [3]i32{ 10, 20, 30 };This creates a fixed array with 3 elements. Each element has type i32.
The full shape is:
[3]i32{ 10, 20, 30 }Read it as:
an array of 3 i32 values, initialized with 10, 20, and 30The Parts of an Array Literal
An array literal has two main parts:
[3]i32{ 10, 20, 30 }The part before the braces is the array type:
[3]i32The part inside the braces is the data:
{ 10, 20, 30 }So this:
const numbers: [3]i32 = [3]i32{ 10, 20, 30 };means:
create a value of type [3]i32 and fill it with these 3 valuesUsually, you do not need to write the type twice.
const numbers = [3]i32{ 10, 20, 30 };This is enough because Zig can infer the variable type from the literal.
Letting Zig Count the Length
You can use _ to ask Zig to count the number of elements:
const numbers = [_]i32{ 10, 20, 30 };This has the same type as:
const numbers = [3]i32{ 10, 20, 30 };The underscore does not mean “unknown forever.” It means “compiler, calculate this for me.”
After compilation, the type is still fixed:
[3]i32This is common Zig style when the literal is written directly in the code.
const vowels = [_]u8{ 'a', 'e', 'i', 'o', 'u' };The compiler sees 5 values, so the type is:
[5]u8Empty Arrays
An array can have length zero.
const empty = [_]u8{};This creates an array with no elements.
Its type is:
[0]u8Zero-length arrays are not common in beginner code, but they are valid. They can appear in generic code, compile-time code, tests, and APIs where “no items” is a valid case.
All Elements Must Match the Element Type
In this array:
const numbers = [_]i32{ 10, 20, 30 };each element must be usable as an i32.
This works:
const numbers = [_]i32{ 1, 2, 3 };This does not work:
const numbers = [_]i32{ 1, 2, "three" };The string "three" is not an i32.
Zig does not silently guess a mixed array type. A fixed array has one element type, and every element must fit that type.
Array Literals with Explicit Variable Types
Sometimes the variable already has a type.
const numbers: [3]i32 = .{ 10, 20, 30 };Here, the literal starts with a dot:
.{ 10, 20, 30 }This means: use the type expected from the left side.
The left side says:
const numbers: [3]i32So Zig knows the literal must be a [3]i32.
This form is very common in Zig.
const rgb: [3]u8 = .{ 255, 128, 0 };The type is written once, on the variable. The initializer uses .{ ... }.
Why .{ ... } Exists
The dot literal is useful because Zig often knows the expected type from context.
For example:
fn printColor(color: [3]u8) void {
_ = color;
}
pub fn main() void {
printColor(.{ 255, 128, 0 });
}The function expects [3]u8, so Zig can understand:
.{ 255, 128, 0 }as:
[3]u8{ 255, 128, 0 }This keeps code shorter without making it unclear.
Array Literals and Mutability
The literal itself is just a value. Mutability depends on the variable you assign it to.
This is immutable:
const numbers = [_]i32{ 10, 20, 30 };You cannot change its elements.
numbers[0] = 99; // errorThis is mutable:
var numbers = [_]i32{ 10, 20, 30 };
numbers[0] = 99;The array now contains:
99, 20, 30The literal syntax does not decide mutability. const or var decides mutability.
Repeating Values
Zig can build an array by repeating a value.
const zeroes = [_]u8{0} ** 8;This creates:
0, 0, 0, 0, 0, 0, 0, 0Its type is:
[8]u8This is useful for buffers:
var buffer = [_]u8{0} ** 1024;This creates 1024 bytes, all initialized to zero.
You can repeat other values too:
const ones = [_]i32{1} ** 5;This creates:
1, 1, 1, 1, 1Concatenating Array Literals
Zig can concatenate arrays using ++.
const a = [_]u8{ 1, 2 };
const b = [_]u8{ 3, 4 };
const c = a ++ b;The result is:
1, 2, 3, 4The type of c is:
[4]u8This also works directly with literals:
const digits = [_]u8{ '0', '1', '2' } ++ [_]u8{ '3', '4', '5' };The result is a single array:
[6]u8Array concatenation happens at compile time when the arrays are known at compile time.
Repetition and Concatenation Together
You can combine ** and ++.
const border = [_]u8{'='} ** 10 ++ [_]u8{'\n'};This creates:
========== newlineMore precisely, it creates 11 bytes: ten = bytes and one newline byte.
This style is useful for constant data.
const header =
[_]u8{'-'} ** 20 ++
[_]u8{'\n'};For large or dynamic strings, you will normally use other tools. But for fixed compile-time data, array literals are simple and efficient.
Nested Array Literals
Arrays can contain arrays.
const matrix = [2][3]i32{
.{ 1, 2, 3 },
.{ 4, 5, 6 },
};This has type:
[2][3]i32Read this as:
an array of 2 rows, where each row is an array of 3 i32 valuesEach row can use dot literal syntax because Zig already knows each row must be [3]i32.
This is equivalent to:
const matrix = [2][3]i32{
[3]i32{ 1, 2, 3 },
[3]i32{ 4, 5, 6 },
};The shorter version is usually better:
const matrix = [2][3]i32{
.{ 1, 2, 3 },
.{ 4, 5, 6 },
};Arrays of Struct Values
Array literals can contain structs.
const Point = struct {
x: i32,
y: i32,
};
const points = [_]Point{
.{ .x = 0, .y = 0 },
.{ .x = 10, .y = 20 },
.{ .x = -5, .y = 3 },
};The type of points is:
[3]PointEach element is a Point.
This is a common pattern for lookup tables, test cases, coordinates, tokens, and configuration data.
Arrays of Anonymous Structs
Zig can also infer an anonymous struct type in some contexts.
const items = .{
.{ .name = "apple", .price = 100 },
.{ .name = "banana", .price = 80 },
};This does not create a normal fixed array in the same simple way as [N]T. It creates a tuple-like value where the fields are known at compile time.
For beginners, prefer explicit array types when you want an array:
const Item = struct {
name: []const u8,
price: u32,
};
const items = [_]Item{
.{ .name = "apple", .price = 100 },
.{ .name = "banana", .price = 80 },
};This is clearer and easier to pass to functions.
Array Literals and Strings
String literals are closely related to arrays of bytes.
const name = "zig";This stores the bytes for:
z i gYou can also write those bytes manually:
const name_bytes = [_]u8{ 'z', 'i', 'g' };But these are not exactly the same type. String literals in Zig have sentinel information, which we will cover later.
For now, the important point is simple: text in Zig is stored as bytes, and array literals are one way to write bytes directly.
Common Mistake: Wrong Length
This is an error:
const numbers = [2]i32{ 10, 20, 30 };The type says 2 elements, but the literal gives 3.
This is also an error:
const numbers = [4]i32{ 10, 20, 30 };The type says 4 elements, but the literal gives 3.
The length and the number of values must match.
Use _ when you want the compiler to count:
const numbers = [_]i32{ 10, 20, 30 };Common Mistake: Confusing Arrays and Slices
This is an array:
const numbers = [_]i32{ 10, 20, 30 };Its type is:
[3]i32This is a slice:
const slice = numbers[0..];Its type is:
[]const i32An array owns the elements. A slice points to elements stored somewhere else.
Array literals create arrays, not slices. You can create a slice from an array after the array exists.
Common Mistake: Forgetting the Dot Literal Needs Context
This works:
const numbers: [3]i32 = .{ 10, 20, 30 };The type is known from the left side.
This may not work in places where Zig has no expected type:
const numbers = .{ 10, 20, 30 };This creates a tuple-like value, not a plain [3]i32.
For a real array, write:
const numbers = [_]i32{ 10, 20, 30 };or:
const numbers: [3]i32 = .{ 10, 20, 30 };A Complete Example
const std = @import("std");
pub fn main() void {
const primes = [_]u32{ 2, 3, 5, 7, 11 };
const zeroes = [_]u8{0} ** 4;
const more_primes = primes ++ [_]u32{ 13, 17 };
std.debug.print("primes:\n", .{});
for (primes, 0..) |value, index| {
std.debug.print(" primes[{}] = {}\n", .{ index, value });
}
std.debug.print("zeroes length = {}\n", .{zeroes.len});
std.debug.print("more_primes:\n", .{});
for (more_primes, 0..) |value, index| {
std.debug.print(" more_primes[{}] = {}\n", .{ index, value });
}
}This program shows three array literal patterns:
const primes = [_]u32{ 2, 3, 5, 7, 11 };A normal array literal with inferred length.
const zeroes = [_]u8{0} ** 4;A repeated array literal.
const more_primes = primes ++ [_]u32{ 13, 17 };A concatenated array.
Summary
An array literal writes array data directly in your code.
The most common forms are:
const a = [3]i32{ 1, 2, 3 };
const b = [_]i32{ 1, 2, 3 };
const c: [3]i32 = .{ 1, 2, 3 };Use [N]T{ ... } when you want to state the length directly.
Use [_]T{ ... } when you want Zig to count the elements.
Use .{ ... } when the expected type is already known.
Array literals are small, direct, and precise. They are one of the first places where Zig’s type system becomes visible: the number of elements and the type of each element are checked before the program runs.