kota's memex

Arrays

Arrays are denoted by ``[N]T, where Nis the number of elements in the array andT` 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.