Skip to main content
Version: Zig 0.13.0

Slices

Slices can be thought of many-item pointers ([*]T) with a length (usize). These use the syntax []T. Slices are easier to use safely and more convinient than many-item pointers, as they store the valid length of the buffer with them. Slices are sometimes referred to as "fat pointers" as they're typically double the size of a normal pointer. Slices are the most common way to pass around buffers in Zig.

Coming from Go?

Slicing in Zig is similar to slicing in Go, but you replace array[start:end] with array[start..end].

Moreover, in Go, there is no explicit ownership or memory management, meaning that slices point to memory owned by the garbage collector. However in Zig, slices point to manually-managed memory; slices are not tied to memory allocation. This has important implications:

  • The validity and lifetime of the backing memory is in the hands of the programmer.
  • Zig slices do not have a Cap field, as they do not resize.

For a resizeable/appendable buffer with ownership, have a look at ArrayList.

Unlike many-item pointers, for loops work on slices.

The syntax x[n..m] is used to create a slice from an array, an operation known as slicing. This creates a "slice" - a view into the array consisting of a pointer and a length. Slicing includes the first element (n), but excludes the last element (m).

In this example, a const slice is used in the total function as it doesn't write to the slice's buffer.

const expect = @import("std").testing.expect;

fn total(values: []const u8) usize {
var sum: usize = 0;
for (values) |v| sum += v;
return sum;
}

test "slices" {
const array = [_]u8{ 1, 2, 3, 4, 5 };
const slice = array[0..3];
try expect(total(slice) == 6);
}

When these n and m values are both known at compile time, slicing will actually produce a pointer to an array. This is not an issue as a pointer to an array i.e. *[N]T will coerce to a slice - []T.

const expect = @import("std").testing.expect;

test "slices 2" {
const array = [_]u8{ 1, 2, 3, 4, 5 };
const slice = array[0..3];
try expect(@TypeOf(slice) == *const [3]u8);
}

The syntax x[n..] can also be used when you want to slice to the end.

test "slices 3" {
var array = [_]u8{ 1, 2, 3, 4, 5 };
var slice = array[0..];
_ = slice;
}

Let's again compare our pointer types.

Feature*T*[N]T[*]T[]T
DereferenceableYes, e.g. ptr.*NoNoNo
IndexableNoYesYesYes
SliceableNoYesYesYes
Element CountAlways 1Compile-time knownUnknownRuntime known
ArithmeticNoNoYes, e.g. ptr + 1 or ptr - 1No
Size@sizeOf(usize)@sizeOf(usize)@sizeOf(usize)@sizeOf(usize) * 2
AttributesYesYesYesYes
info

We can now apply our knowledge and make another program together.