A slice is a view into an array.
An array owns its storage. A slice refers to existing storage.
Given an array:
const a = [_]i32{ 10, 20, 30, 40, 50 };A slice is created with the slicing operator:
const s = a[1..4];The slice contains:
20 30 40The lower bound is included. The upper bound is excluded.
So:
a[1..4]means elements:
a[1]
a[2]
a[3]The type of s is not an array type. It is a slice type:
[]const i32This means:
a slice of read-only i32 valuesThe slice stores two things:
- A pointer to the first element.
- A length.
The slice does not copy the array.
This program demonstrates it:
const std = @import("std");
pub fn main() void {
var a = [_]i32{ 10, 20, 30, 40, 50 };
const s = a[1..4];
a[2] = 999;
std.debug.print("{d}\n", .{s[1]});
}The output is:
999The slice refers to the original array storage.
A slice has a .len field:
const std = @import("std");
pub fn main() void {
const a = [_]u8{ 1, 2, 3, 4, 5 };
const s = a[1..4];
std.debug.print("{d}\n", .{s.len});
}The output is:
3Slices may be indexed like arrays:
const std = @import("std");
pub fn main() void {
const a = [_]i32{ 100, 200, 300, 400 };
const s = a[1..3];
std.debug.print("{d}\n", .{s[0]});
std.debug.print("{d}\n", .{s[1]});
}The output is:
200
300The index is relative to the slice, not the original array.
A slice may cover the whole array:
const s = a[0..a.len];Since this is common, Zig provides shorthand:
const s = a[0..];This means:
from index 0 to the endAnother example:
const s = a[2..];This means:
from index 2 to the endA slice may also be empty:
const s = a[2..2];Its length is zero.
Slices are commonly passed to functions because the function does not need to know the array size at compile time.
This function prints any slice of integers:
const std = @import("std");
fn printSlice(s: []const i32) void {
for (s) |x| {
std.debug.print("{d}\n", .{x});
}
}
pub fn main() void {
const a = [_]i32{ 1, 2, 3, 4, 5 };
printSlice(a[1..4]);
}The output is:
2
3
4The parameter type:
[]const i32means:
a slice of read-only i32 valuesThe function accepts slices from arrays of any length.
Mutable slices are also possible:
const std = @import("std");
fn double(s: []i32) void {
for (s) |*x| {
x.* *= 2;
}
}
pub fn main() void {
var a = [_]i32{ 1, 2, 3, 4 };
double(a[1..3]);
for (a) |x| {
std.debug.print("{d}\n", .{x});
}
}The output is:
1
4
6
4The slice modified the original array.
Slices are one of the most important types in Zig. Much of the standard library uses them for strings, buffers, file data, and collections.
An array has its size in the type:
[5]u8A slice does not:
[]u8This difference is fundamental.
Arrays are fixed-size values. Slices are run-time views into memory.
Exercises.
Exercise 6-9. Create an array of 10 integers and make a slice containing the middle four elements.
Exercise 6-10. Write a function that computes the sum of a slice of integers.
Exercise 6-11. Write a function that sets every element of a mutable slice to zero.
Exercise 6-12. Create two slices referring to different parts of the same array and show that modifying the array affects both slices.