Skip to content

`std.mem`

The std.mem module contains operations on memory, slices, bytes, and basic data movement. Much of Zig programming eventually passes through std.mem.

std.mem

The std.mem module contains operations on memory, slices, bytes, and basic data movement. Much of Zig programming eventually passes through std.mem.

A simple example copies bytes from one array into another.

const std = @import("std");

pub fn main() void {
    var source = [_]u8{ 1, 2, 3, 4 };
    var dest = [_]u8{ 0, 0, 0, 0 };

    @memcpy(dest[0..], source[0..]);

    std.debug.print("{any}\n", .{dest});
}

The output is:

{ 1, 2, 3, 4 }

Slices are central to std.mem.

A slice is a pointer and a length:

[]u8

This means: “a runtime-sized slice of writable bytes.”

A constant slice is:

[]const u8

Many functions in std.mem operate on slices rather than raw pointers.

A string in Zig is usually a slice of bytes:

const message = "hello";

The type is:

*const [5:0]u8

but it automatically coerces to:

[]const u8

when needed.

A common operation is comparing slices.

const std = @import("std");

pub fn main() void {
    const a = "zig";
    const b = "zig";

    const equal = std.mem.eql(u8, a, b);

    std.debug.print("{any}\n", .{equal});
}

The output is:

true

std.mem.eql compares elements one by one.

The first argument is the element type:

u8

This tells Zig how to compare the slice contents.

Searching slices is also common.

const std = @import("std");

pub fn main() void {
    const text = "hello zig";

    if (std.mem.indexOf(u8, text, "zig")) |index| {
        std.debug.print("found at {d}\n", .{index});
    }
}

The output is:

found at 6

indexOf returns an optional value.

If the substring exists, the index is returned.

If not, the result is null.

Many std.mem functions follow this style.

Memory initialization is another common task.

const std = @import("std");

pub fn main() void {
    var buffer: [8]u8 = undefined;

    @memset(buffer[0..], 0);

    std.debug.print("{any}\n", .{buffer});
}

The output is:

{ 0, 0, 0, 0, 0, 0, 0, 0 }

undefined means the memory initially contains unspecified values.

Zig does not automatically clear memory.

This avoids hidden work and keeps costs visible.

@memset explicitly fills memory with a value.

Slices can also be split.

const std = @import("std");

pub fn main() void {
    const text = "abcdef";

    const left = text[0..3];
    const right = text[3..];

    std.debug.print("{s}\n", .{left});
    std.debug.print("{s}\n", .{right});
}

The output is:

abc
def

Slicing does not allocate memory.

The slices point into the original data.

Many operations in Zig avoid allocation unless explicitly requested.

std.mem also provides functions for byte order conversion, alignment calculations, tokenization, and parsing support.

For example:

FunctionPurpose
std.mem.eqlCompare slices
std.mem.indexOfSearch slices
std.mem.startsWithPrefix check
std.mem.endsWithSuffix check
std.mem.splitSequenceSplit slices
std.mem.trimRemove bytes from edges
std.mem.concatConcatenate with allocation

A tokenizer example:

const std = @import("std");

pub fn main() void {
    const text = "red,green,blue";

    var it = std.mem.splitSequence(u8, text, ",");

    while (it.next()) |part| {
        std.debug.print("{s}\n", .{part});
    }
}

The output is:

red
green
blue

The iterator returns slices into the original string.

No copying occurs.

This is typical Zig design: explicit allocation, direct memory access, and predictable cost.

Exercise 14-5. Compare two strings with std.mem.eql.

Exercise 14-6. Search for a substring with std.mem.indexOf.

Exercise 14-7. Write a tokenizer that splits words separated by spaces.

Exercise 14-8. Allocate an array, fill it with a value using @memset, and print the result.