Slices
test.zig
const expect = @import("std").testing.expect;
test "basic slices" {
var array = [_]i32{ 1, 2, 3, 4 };
// A slice is a pointer and a length. The difference between an array and
// a slice is that the array's length is part of the type and known at
// compile-time, whereas the slice's length is known at runtime.
// Both can be accessed with the `len` field.
var known_at_runtime_zero: usize = 0;
const slice = array[known_at_runtime_zero..array.len];
try expect(@TypeOf(slice) == []i32);
try expect(&slice[0] == &array[0]);
try expect(slice.len == array.len);
// If you slice with comptime-known start and end positions, the result is
// a pointer to an array, rather than a slice.
const array_ptr = array[0..array.len];
try expect(@TypeOf(array_ptr) == *[array.len]i32);
// Using the address-of operator on a slice gives a single-item pointer,
// while using the `ptr` field gives a many-item pointer.
try expect(@TypeOf(slice.ptr) == [*]i32);
try expect(@TypeOf(&slice[0]) == *i32);
try expect(@ptrToInt(slice.ptr) == @ptrToInt(&slice[0]));
// Slices have array bounds checking. If you try to access something out
// of bounds, you'll get a safety check failure:
slice[10] += 1;
// Note that `slice.ptr` does not invoke safety checking, while `&slice[0]`
// asserts that the slice has len >= 1.
}
Shell
$ zig test test.zig
1/1 test.basic slices... thread 3567298 panic: index out of bounds: index 10, len 4
docgen_tmp/test.zig:28:10: 0x2119c6 in test.basic slices (test)
slice[10] += 1;
^
/home/ci/release-0.10.1/out/zig-x86_64-linux-musl-baseline/lib/zig/test_runner.zig:63:28: 0x2131b3 in main (test)
} else test_fn.func();
^
/home/ci/release-0.10.1/out/zig-x86_64-linux-musl-baseline/lib/zig/std/start.zig:604:22: 0x21233c in posixCallMainAndExit (test)
root.main();
^
/home/ci/release-0.10.1/out/zig-x86_64-linux-musl-baseline/lib/zig/std/start.zig:376:5: 0x211e41 in _start (test)
@call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
^
error: the following test command crashed:
/home/ci/release-0.10.1/out/zig-local-cache/o/886dbde2c2a21074c6c6d3ff9b83336b/test
This is one reason we prefer slices to pointers.
slices.zig
const std = @import("std");
const expect = std.testing.expect;
const mem = std.mem;
const fmt = std.fmt;
test "using slices for strings" {
// Zig has no concept of strings. String literals are const pointers
// to null-terminated arrays of u8, and by convention parameters
// that are "strings" are expected to be UTF-8 encoded slices of u8.
// Here we coerce *const [5:0]u8 and *const [6:0]u8 to []const u8
const hello: []const u8 = "hello";
const world: []const u8 = "世界";
var all_together: [100]u8 = undefined;
// You can use slice syntax on an array to convert an array into a slice.
const all_together_slice = all_together[0..];
// String concatenation example.
const hello_world = try fmt.bufPrint(all_together_slice, "{s} {s}", .{ hello, world });
// Generally, you can use UTF-8 and not worry about whether something is a
// string. If you don't need to deal with individual characters, no need
// to decode.
try expect(mem.eql(u8, hello_world, "hello 世界"));
}
test "slice pointer" {
var a: []u8 = undefined;
try expect(@TypeOf(a) == []u8);
var array: [10]u8 = undefined;
const ptr = &array;
try expect(@TypeOf(ptr) == *[10]u8);
// A pointer to an array can be sliced just like an array:
var start: usize = 0;
var end: usize = 5;
const slice = ptr[start..end];
slice[2] = 3;
try expect(slice[2] == 3);
// The slice is mutable because we sliced a mutable pointer.
try expect(@TypeOf(slice) == []u8);
// Again, slicing with constant indexes will produce another pointer to an array:
const ptr2 = slice[2..3];
try expect(ptr2.len == 1);
try expect(ptr2[0] == 3);
try expect(@TypeOf(ptr2) == *[1]u8);
}
Shell
$ zig test slices.zig
1/2 test.using slices for strings... OK
2/2 test.slice pointer... OK
All 2 tests passed.
See also:
Sentinel-Terminated Slices
The syntax [:x]T
is a slice which has a runtime known length and also guarantees a sentinel value at the element indexed by the length. The type does not guarantee that there are no sentinel elements before that. Sentinel-terminated slices allow element access to the len
index.
null_terminated_slice.zig
const std = @import("std");
const expect = std.testing.expect;
test "null terminated slice" {
const slice: [:0]const u8 = "hello";
try expect(slice.len == 5);
try expect(slice[5] == 0);
}
Shell
$ zig test null_terminated_slice.zig
1/1 test.null terminated slice... OK
All 1 tests passed.
Sentinel-terminated slices can also be created using a variation of the slice syntax data[start..end :x]
, where data
is a many-item pointer, array or slice and x
is the sentinel value.
null_terminated_slicing.zig
const std = @import("std");
const expect = std.testing.expect;
test "null terminated slicing" {
var array = [_]u8{ 3, 2, 1, 0, 3, 2, 1, 0 };
var runtime_length: usize = 3;
const slice = array[0..runtime_length :0];
try expect(@TypeOf(slice) == [:0]u8);
try expect(slice.len == 3);
}
Shell
$ zig test null_terminated_slicing.zig
1/1 test.null terminated slicing... OK
All 1 tests passed.
Sentinel-terminated slicing asserts that the element in the sentinel position of the backing data is actually the sentinel value. If this is not the case, safety-protected Undefined Behavior results.
test.zig
const std = @import("std");
const expect = std.testing.expect;
test "sentinel mismatch" {
var array = [_]u8{ 3, 2, 1, 0 };
// Creating a sentinel-terminated slice from the array with a length of 2
// will result in the value `1` occupying the sentinel element position.
// This does not match the indicated sentinel value of `0` and will lead
// to a runtime panic.
var runtime_length: usize = 2;
const slice = array[0..runtime_length :0];
_ = slice;
}
Shell
$ zig test test.zig
1/1 test.sentinel mismatch... thread 3567804 panic: sentinel mismatch: expected 0, found 1
docgen_tmp/test.zig:12:24: 0x211604 in test.sentinel mismatch (test)
const slice = array[0..runtime_length :0];
^
/home/ci/release-0.10.1/out/zig-x86_64-linux-musl-baseline/lib/zig/test_runner.zig:63:28: 0x212fc3 in main (test)
} else test_fn.func();
^
/home/ci/release-0.10.1/out/zig-x86_64-linux-musl-baseline/lib/zig/std/start.zig:604:22: 0x211f6c in posixCallMainAndExit (test)
root.main();
^
/home/ci/release-0.10.1/out/zig-x86_64-linux-musl-baseline/lib/zig/std/start.zig:376:5: 0x211a71 in _start (test)
@call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
^
error: the following test command crashed:
/home/ci/release-0.10.1/out/zig-local-cache/o/886dbde2c2a21074c6c6d3ff9b83336b/test
See also: