Arrays
Arrays are denoted by ``[N]T, where
Nis the number of elements in the array and
T` is the type of those elements.
Infer size
For array literals, N
may be replaced by _
to infer the size of the array.
const a = [5]u8{ 'h', 'e', 'l', 'l', 'o' };
const b = [_]u8{ 'w', 'o', 'r', 'l', 'd' };
Length
To get the size of an array access its len
field:
const array = [_]u8{ 'h', 'e', 'l', 'l', 'o' };
const length = array.len; // 5
Slices
When we slice an array with a non-compile time length we are given a slice which has a length and a pointer to a backing array. It's better to think of them more as pointers than as slices in the go sense.
const a = [_]i32{1, 2, 3, 4, 5};
var end: usize = 4;
const b = a[1..end];
In this example b is of type: []const i32
That example is quite contrived. If we omit the variable end
and instead write
the constant 4
in the initialization for b
, zig will optimize this code and
b's type will instead become: *const [3]i32
which is simply a pointer to an
array of length 3
instead of a slice.
Another thing to note is that even if we made b
a var
instead of const
we
would get a compile error if we try to assign its fields. This is because var
in the context of a slice refers to us being able to reuse the name to point to
a different array. Being able to edit the underlying array depends on the array
itself being const
or var
. You could always allocate a new array.
Confirming the type can be done with: std.debug.print("{any}", .{@TypeOf(b)});
In real code, you'll likely use slices more than arrays. For better or worse, programs tend to have more runtime information than compile time information.
Strings
By convention a string in zig is UTF-8 encoded, but this is not enforced and
there's no type difference from a []const u8
representing a UTF-8 string and
one representing binary data.
String literals
When we declare a string literal in the code it is stored in the binary itself. String literals have a fixed length and are always NULL terminated. Null-terminated strings are important when interacting with C.
The underlying "baked in" type for the string literal "kota" or rather
{'k', 'o', 't', 'a', 0}
would be: *const [4:0]u8
.
The 4
in this case is the length without the null terminator. The value after
the colon is the "sentinal" which is the special value marking the end of the
array. In our case, and the case with almost every string, it's 0
which is the
null terminator. You could technically declare something like this:
const a = [3:false]bool{false, true, false};
Type Coercion
We can write []const u8
for a string field of a struct and then pass in a
string literal of type *const [4:0]u8
and zig will coerce the type. Zig does
this with a few types, but it's most obvious with strings. Because null
terminated strings are arrays, and arrays have a known length, this coercion is
cheap, i.e. it does not require iterating through the string to find the null
terminator.