Arrays
In Zig, we declare an array like this:
const name: [array length]type = .{ comma-separated values };
o
const name = [_ o length] type{ comma-separated values };
An array is a contiguous sequence of elements of the same type. This sequence always has a fixed length, which can be either:
- inferred by Zig from the elements (writing _ instead of the length), or
- explicitly declared - specifying the array size and the type.
array.zig |
|
const std = @import("std"); const print = std.debug.print; pub fn main() void { // size inferred from data and type const a_numbers = [_]u8{ 1, 2, 3, 4, 5 }; // size declared explicitly const a_numbers2: [5]u8 = .{ 1, 2, 3, 4, 5 }; print("Length of a_numbers is: {}.\n", .{a_numbers.len}); print("a_numbers2 printed is: {any}.\n", .{a_numbers2}); } |
|
$ zig run array.zig |
|
Length of a_numbers is: 5. a_numbers2 printed is: { 1, 2, 3, 4, 5 }. |
To display an array, we used the {any} format specifier in the printed string.
Array length
Arrays in Zig have a property called .len (short for length), which we can access as shown in the previous example.
This property is an unsigned integer. If we dig a bit deeper into it, we’ll understand an important detail:
array_size.zig |
|
const std = @import("std"); const print = std.debug.print; pub fn main() void { const a_numbers_1 = [_]u8{ 2, 18, 78, 1, 3 }; const a_numbers_2 = [_]u16{ 8, 5, 10, 6, 12 }; const n_len_1 = a_numbers_1.len; const n_len_2 = a_numbers_2.len; // check array lengths print("a_numbers_1.len = {} elements \n", .{n_len_1}); print("a_numbers_2.len = {} elements \n\n", .{n_len_2}); // check array types print("a_numbers_1 type = {} \n", .{@TypeOf(a_numbers_1)}); print("a_numbers_2 type = {} \n\n", .{@TypeOf(a_numbers_2)}); // check size of one element print("a_numbers_1 element size = {d} bytes\n", .{@sizeOf(@TypeOf(a_numbers_1[0]))}); print("a_numbers_2 element size = {d} bytes\n\n", .{@sizeOf(@TypeOf(a_numbers_2[0]))}); // type of the length value print("a_numbers_1.len type = {} \n", .{@TypeOf(n_len_1)}); print("a_numbers_2.len type = {} \n\n", .{@TypeOf(n_len_2)}); // size of the length value print("a_numbers_1.len = {} bytes \n", .{@sizeOf(@TypeOf(n_len_1))}); print("a_numbers_2.len = {} bytes.\n", .{@sizeOf(@TypeOf(n_len_2))}); } |
|
$ zig run array_size.zig |
|
a_numbers_1.len = 5 elements a_numbers_2.len = 5 elements a_numbers_1 type = [5]u8 a_numbers_2 type = [5]u16 a_numbers_1 element size = 1 byte a_numbers_2 element size = 2 bytes a_numbers_1.len type = usize a_numbers_2.len type = usize a_numbers_1.len size = 8 bytes a_numbers_2.len size = 8 bytes. |
We defined two arrays: a_numbers_1 and a_numbers_2. Both arrays have the same number of elements but different element types:
- one contains 8-bit (1 byte) elements
- the other contains 16-bit (2 bytes each) elements
When we access their .len, it gives us the number of elements: 5. These two arrays are equal in terms of length, not in terms of how many bytes they occupy in memory. The value returned by .len has a special data type: usize.
Zig reserves the usize type for:
- counting elements in arrays and slices
- representing memory sizes
- navigating through memory positions or data structures
Additionally, the size of the usize type depends on the system:
- On 32-bit systems, usize is equivalent to u32
- On 64-bit systems, it's equivalent to u64
How to check the type and size of a value
To check the type of an element or structure, we use Zig’s built-in function @TypeOf. The @ prefix indicates that it’s a builtin function (an intrinsic part of the language) just like @import, which we use to import the standard library std.
The function we're using to get the size of a type is @sizeOf. And note: this function doesn't operate on a value directly, but on its type - which we obtain beforehand using @TypeOf. For example, if we tried to call it like this, it would result in an error:
|
@sizeOf(a_numbers_1[0]) |
error: expected type 'type', found 'u8'
The @sizeOf function expects a type as its argument:
|
@sizeOf(@TypeOf(a_numbers_1[0])) |