Lesson 5 / 25

Arrays, Slices and Strings

Use fixed arrays, slices and byte strings safely.

Arrays know their length; slices carry it

An array [N]T has a compile-time-known length that is part of its type: [3]u32. Literals can infer the length with [_]u32{ 1, 2, 3 }, and ** repeats ([_]u8{0} ** 16). Arrays are values: assigning one copies it. A slice []T (or []const T) is a pointer plus a length, created by slicing (array[1..3], array[2..]) or from allocations; indexing is bounds-checked in safe modes, so out-of-range access panics instead of corrupting memory. Strings are just bytes: []const u8, conventionally UTF-8, with no separate string type. String literals are null-terminated arrays (*const [N:0]u8) that coerce to []const u8; the :0 is a sentinel, useful for C interop ([:0]const u8). The std.mem module provides the operations: std.mem.eql(u8, a, b) to compare (never == on slices), indexOf, startsWith, splitScalar, tokenizeScalar, trim and concat; std.fmt.parseInt parses numbers and std.unicode handles code points. Multi-line string literals use \\ at the start of each line.

Slices and string handling

Bounds-checked slicing, std.mem helpers and parsing.

const std = @import("std");

pub fn main() !void {
    var scores = [_]u32{ 72, 91, 58, 84, 66 };
    const top_three: []u32 = scores[0..3];          // pointer + length into the array
    top_three[0] = 75;                              // modifies scores[0]

    var sum: u32 = 0;
    for (scores) |s| sum += s;
    std.debug.print("sum={d} slice len={d} first={d}\n", .{ sum, top_three.len, scores[0] });

    const line = "  o-17, Pune , 120000  ";
    var fields = std.mem.splitScalar(u8, std.mem.trim(u8, line, " "), ',');
    const order_id = std.mem.trim(u8, fields.next().?, " ");
    const city = std.mem.trim(u8, fields.next().?, " ");
    const total = try std.fmt.parseInt(u64, std.mem.trim(u8, fields.next().?, " "), 10);

    std.debug.print("{s} | {s} | {d}\n", .{ order_id, city, total });
    std.debug.print("is Pune? {}\n", .{std.mem.eql(u8, city, "Pune")});   // compare contents, not pointers
    std.debug.print("starts with o-? {}\n", .{std.mem.startsWith(u8, order_id, "o-")});

    const greeting =
        \\Dear customer,
        \\Your order has shipped.
    ;
    std.debug.print("{s}\n", .{greeting});
    // scores[7] would panic: index out of bounds (in Debug and ReleaseSafe)
}

A window onto a shelf

An array is the whole shelf of fixed size. A slice is a window you hold up to part of the shelf: it knows where it starts and how wide it is, and in safe builds the window refuses to look past the shelf's edge.

Quick check: How should you compare the contents of two strings in Zig?

  • With ==
  • With std.mem.eql(u8, a, b)
  • With strcmp
  • They cannot be compared
Answer

With std.mem.eql(u8, a, b) — Slices are pointer and length; std.mem.eql compares their bytes.