std.mem (Memory Utilities)
The Zig standard library module std.mem provides memory operation utilities for slices and byte sequences, including copying, initialization, comparison, and searching. It offers type-safe equivalents of C's memcpy, memset, and memcmp, along with higher-level operations such as prefix matching, suffix matching, and substring searching. Because it requires no allocator — you simply pass slices — it is useful for everything from low-level buffer manipulation to everyday string processing.
Syntax
// -----------------------------------------------
// Syntax overview for major std.mem functions
// -----------------------------------------------
const std = @import("std");
const mem = std.mem;
// -----------------------------------------------
// copy — copy a slice
// -----------------------------------------------
// copy(T, dst, src) — copies the contents of src into dst
// dst.len >= src.len is required
var dst: [4]u8 = undefined;
const src: []const u8 = "Zig!";
@memcpy(&dst, src); // @memcpy is recommended from Zig 0.12 onward
mem.copyForwards(u8, &dst, src); // forward copy (not safe for overlapping regions)
mem.copyBackwards(u8, &dst, src); // backward copy (use when regions overlap)
// -----------------------------------------------
// set / @memset — fill bytes in bulk
// -----------------------------------------------
// @memset(slice, value) — fills every element with value
var buf: [8]u8 = undefined;
@memset(&buf, 0); // zero-clears all bytes
// -----------------------------------------------
// eql — compare slices for equality
// -----------------------------------------------
// eql(T, a, b) — returns true if a and b are equal element by element
const ok = mem.eql(u8, "goku", "goku"); // true
// -----------------------------------------------
// startsWith / endsWith — prefix and suffix matching
// -----------------------------------------------
// startsWith(T, haystack, needle) — checks whether haystack begins with needle
const starts = mem.startsWith(u8, "goku_ssj", "goku"); // true
// endsWith(T, haystack, needle) — checks whether haystack ends with needle
const ends = mem.endsWith(u8, "goku_ssj", "ssj"); // true
// -----------------------------------------------
// indexOf / lastIndexOf — searching
// -----------------------------------------------
// indexOf(T, haystack, needle) — returns the offset of the first match
// returns null if not found
const pos = mem.indexOf(u8, "vegeta_ssj", "_"); // 6
// lastIndexOf(T, haystack, needle) — searches from the end
const lpos = mem.lastIndexOf(u8, "a_b_c", "_"); // 3
// -----------------------------------------------
// tokenizeAny / splitAny — splitting
// -----------------------------------------------
// tokenizeAny(T, buf, delimiters) — splits into tokens on any delimiter character
var it = mem.tokenizeAny(u8, "goku,vegeta,gohan", ",");
while (it.next()) |token| {
// token is a slice (no copy made)
_ = token;
}
// -----------------------------------------------
// bytesAsSlice / sliceAsBytes — type reinterpretation
// -----------------------------------------------
// bytesAsSlice(T, bytes) — reinterprets a byte slice as a slice of T
var raw: [8]u8 = .{ 1, 0, 0, 0, 2, 0, 0, 0 };
const u32_slice = mem.bytesAsSlice(u32, &raw); // []u32, length 2
// sliceAsBytes(slice) — reinterprets a slice of T as a byte slice
const u32_data: []const u32 = &.{ 100, 200 };
const bytes = mem.sliceAsBytes(u32_data); // []const u8, length 8
Key Functions
| Function / Built-in | Description |
|---|---|
@memcpy(dst, src) | Copies the contents of src into dst. This is the recommended approach from Zig 0.12 onward; overlapping regions result in undefined behavior. |
mem.copyForwards(T, dst, src) | Copies from low address to high address. Use when src and dst do not overlap. |
mem.copyBackwards(T, dst, src) | Copies from high address to low address. Use when you need to safely copy overlapping regions. |
@memset(slice, value) | Fills every element of the slice with value. Useful for zero-clearing or bulk initialization. |
mem.eql(T, a, b) | Compares two slices element by element for equality. Frequently used for string comparison. |
mem.startsWith(T, haystack, needle) | Returns whether haystack begins with needle. The return type is bool. |
mem.endsWith(T, haystack, needle) | Returns whether haystack ends with needle. The return type is bool. |
mem.indexOf(T, haystack, needle) | Returns the position of the first occurrence of needle in haystack. Returns null if not found. |
mem.lastIndexOf(T, haystack, needle) | Searches from the end and returns the position of the last occurrence of needle. Returns null if not found. |
mem.tokenizeAny(T, buf, delimiters) | Returns an iterator that splits the buffer into tokens on any of the delimiter characters. Consecutive delimiters are skipped. |
mem.splitAny(T, buf, delimiters) | Returns an iterator that splits on delimiter characters. Unlike tokenizeAny, empty tokens are also returned. |
mem.bytesAsSlice(T, bytes) | Reinterprets a byte slice as a slice of another type. The byte count must be divisible by the size of T. |
mem.sliceAsBytes(slice) | Reinterprets a slice of any type as a byte slice. Useful for low-level serialization. |
Sample Code
dragonball_std_mem.zig
// dragonball_std_mem.zig — demonstrates the major memory operation
// utilities in std.mem using Dragon Ball characters
//
// Build:
// zig run dragonball_std_mem.zig
const std = @import("std");
const mem = std.mem;
pub fn main() !void {
// Get a writer for standard output
const stdout = std.io.getStdOut().writer();
// -----------------------------------------------
// @memcpy — copy a slice
// -----------------------------------------------
try stdout.print("=== slice copy with @memcpy ===\n\n", .{});
// Copy Goku's name into a buffer
const goku_name = "孫悟空";
var name_buf: [goku_name.len]u8 = undefined;
@memcpy(&name_buf, goku_name);
try stdout.print(" source: {s}\n", .{goku_name});
try stdout.print(" dest: {s}\n\n", .{name_buf});
// -----------------------------------------------
// @memset — zero-clear a buffer
// -----------------------------------------------
try stdout.print("=== zero-clear with @memset ===\n\n", .{});
// Initialize a power level buffer to zero
var power_buf: [8]u8 = .{ 1, 2, 3, 4, 5, 6, 7, 8 };
try stdout.print(" before: {any}\n", .{power_buf});
@memset(&power_buf, 0);
try stdout.print(" after: {any}\n\n", .{power_buf});
// -----------------------------------------------
// mem.eql — compare slices
// -----------------------------------------------
try stdout.print("=== slice comparison with mem.eql ===\n\n", .{});
const hero = "孫悟空";
const rival = "ベジータ";
const hero2 = "孫悟空"; // same content as hero
// Returns true when the contents are equal (not a pointer comparison)
const same = mem.eql(u8, hero, hero2);
const differ = mem.eql(u8, hero, rival);
try stdout.print(" eql(\"{s}\", \"{s}\") = {}\n", .{ hero, hero2, same });
try stdout.print(" eql(\"{s}\", \"{s}\") = {}\n\n", .{ hero, rival, differ });
// -----------------------------------------------
// mem.startsWith / endsWith — prefix and suffix matching
// -----------------------------------------------
try stdout.print("=== mem.startsWith / endsWith ===\n\n", .{});
// Check prefix and suffix on a character codename
const codename = "ssj3_goku_kamehameha";
const sw1 = mem.startsWith(u8, codename, "ssj3"); // true
const sw2 = mem.startsWith(u8, codename, "ssj4"); // false
const ew1 = mem.endsWith(u8, codename, "kamehameha"); // true
const ew2 = mem.endsWith(u8, codename, "galick_gun"); // false
try stdout.print(" target: \"{s}\"\n", .{codename});
try stdout.print(" startsWith(\"ssj3\") = {}\n", .{sw1});
try stdout.print(" startsWith(\"ssj4\") = {}\n", .{sw2});
try stdout.print(" endsWith(\"kamehameha\") = {}\n", .{ew1});
try stdout.print(" endsWith(\"galick_gun\") = {}\n\n", .{ew2});
// -----------------------------------------------
// mem.indexOf / lastIndexOf — searching
// -----------------------------------------------
try stdout.print("=== mem.indexOf / lastIndexOf ===\n\n", .{});
// Search for separator positions in Goku's technique list
const techniques = "kamehameha_kaioken_spirit_bomb";
const first_sep = mem.indexOf(u8, techniques, "_"); // position of the first "_"
const last_sep = mem.lastIndexOf(u8, techniques, "_"); // position of the last "_"
try stdout.print(" target: \"{s}\"\n", .{techniques});
if (first_sep) |pos| {
try stdout.print(" indexOf(\"_\") = {} (just after \"{s}\")\n",
.{ pos, techniques[0..pos] });
}
if (last_sep) |pos| {
try stdout.print(" lastIndexOf(\"_\") = {} (just before \"{s}\")\n\n",
.{ pos, techniques[0..pos] });
}
// -----------------------------------------------
// mem.tokenizeAny — tokenize a string
// -----------------------------------------------
try stdout.print("=== tokenization with mem.tokenizeAny ===\n\n", .{});
// Split the Z Fighters list on commas
const warriors = "悟空,ベジータ,ピッコロ,クリリン,ゴハン";
try stdout.print(" input: \"{s}\"\n", .{warriors});
var iter = mem.tokenizeAny(u8, warriors, ",");
var index: usize = 0;
while (iter.next()) |token| {
try stdout.print(" [{d}] {s}\n", .{ index, token });
index += 1;
}
// -----------------------------------------------
// mem.copyForwards — forward copy
// -----------------------------------------------
try stdout.print("\n=== forward copy with mem.copyForwards ===\n\n", .{});
// Copy Vegeta's power level scores into another buffer
const vegeta_scores: []const u8 = &.{ 18, 000, 180, 000, 250, 000 };
var score_copy: [6]u8 = undefined;
mem.copyForwards(u8, &score_copy, vegeta_scores);
try stdout.print(" source: {any}\n", .{vegeta_scores});
try stdout.print(" dest: {any}\n\n", .{score_copy});
// -----------------------------------------------
// mem.bytesAsSlice — reinterpret bytes as a u32 slice
// -----------------------------------------------
try stdout.print("=== type reinterpretation with mem.bytesAsSlice ===\n\n", .{});
// Represent Frieza's power level as a little-endian u32 byte sequence
// 120,000,000 = 0x0727_5B00
var freeza_bytes: [4]u8 align(4) = .{ 0x00, 0x5B, 0x27, 0x07 };
const freeza_u32 = mem.bytesAsSlice(u32, &freeza_bytes);
try stdout.print(" bytes: {any}\n", .{freeza_bytes});
try stdout.print(" as u32: {d} (Frieza's final power level)\n\n", .{freeza_u32[0]});
try stdout.print("Done. Kamehameha!\n", .{});
}
zig run dragonball_std_mem.zig
=== slice copy with @memcpy ===
source: 孫悟空
dest: 孫悟空
=== zero-clear with @memset ===
before: { 1, 2, 3, 4, 5, 6, 7, 8 }
after: { 0, 0, 0, 0, 0, 0, 0, 0 }
=== slice comparison with mem.eql ===
eql("孫悟空", "孫悟空") = true
eql("孫悟空", "ベジータ") = false
=== mem.startsWith / endsWith ===
target: "ssj3_goku_kamehameha"
startsWith("ssj3") = true
startsWith("ssj4") = false
endsWith("kamehameha") = true
endsWith("galick_gun") = false
=== mem.indexOf / lastIndexOf ===
target: "kamehameha_kaioken_spirit_bomb"
indexOf("_") = 10 (just after "kamehameha")
lastIndexOf("_") = 18 (just before "kamehameha_kaioken")
=== tokenization with mem.tokenizeAny ===
input: "悟空,ベジータ,ピッコロ,クリリン,ゴハン"
[0] 悟空
[1] ベジータ
[2] ピッコロ
[3] クリリン
[4] ゴハン
=== forward copy with mem.copyForwards ===
source: { 18, 0, 180, 0, 250, 0 }
dest: { 18, 0, 180, 0, 250, 0 }
=== type reinterpretation with mem.bytesAsSlice ===
bytes: { 0, 91, 39, 7 }
as u32: 120000256 (Frieza's final power level)
Done. Kamehameha!
Overview
Zig's std.mem is a utility module for memory and string operations on slices. For copying, choose @memcpy (non-overlapping), mem.copyForwards, or mem.copyBackwards (overlapping) as appropriate. For initialization, @memset fills an entire slice in one call. For comparison, mem.eql performs element-wise equality checking, and mem.startsWith / mem.endsWith handle prefix and suffix matching. For substring searching, use mem.indexOf / mem.lastIndexOf; results are returned as ?usize (Optional) and must be unwrapped with if (pos) |p|. For splitting strings, mem.tokenizeAny and mem.splitAny return iterators without requiring an allocator. For type reinterpretation, mem.bytesAsSlice / mem.sliceAsBytes convert between byte slices and slices of any type. For more advanced memory management, see Allocator Basics; for string formatting, see String Formatting.
If you find any errors or copyright issues, please contact us.