zig-skills/references/std-segmented-list.md

3.3 KiB

std.SegmentedList - removed in Zig 0.16.0

Primary Zig 0.16 release-note source: https://ziglang.org/download/0.16.0/release-notes.html

std.SegmentedList was removed in Zig 0.16. Do not use the API below in new code. Choose a project-owned segmented container, an arena-backed structure, an index-based std.ArrayList, or another stable-handle design depending on the ownership/lifetime requirement.

A dynamic list where element pointers remain stable across growth. Unlike ArrayList, appending never invalidates existing pointers. Elements are stored in exponentially-sized segments.

When to Use

  • Need stable pointers to elements (pointers survive append)
  • Arena allocator backing (avoids wasted memory on reallocation)
  • Non-copyable element types
  • Stack-like access patterns (append/pop)

Trade-offs

  • Elements not contiguous (most are, but not guaranteed)
  • O(log n) random access (vs O(1) for ArrayList)
  • Higher per-element overhead

Initialization

// Without preallocation
var list = std.SegmentedList(i32, 0){};
defer list.deinit(allocator);

// With preallocation (must be power of 2)
// First N elements stored inline, no allocation needed
var list = std.SegmentedList(i32, 16){};
defer list.deinit(allocator);

Basic Operations

// Append (pointer remains valid forever)
try list.append(allocator, 42);
try list.appendSlice(allocator, &[_]i32{ 1, 2, 3 });

// Get pointer to element (STABLE across appends)
const ptr = list.at(0);  // *i32
ptr.* = 100;             // modify in place

// Add and get pointer in one operation
const new_ptr = try list.addOne(allocator);
new_ptr.* = 42;

// Pop
const last = list.pop();  // ?i32

// Length
const n = list.count();
// Or: list.len

Iteration

// Forward iteration with mutable access
var it = list.iterator(0);  // start at index 0
while (it.next()) |ptr| {
    ptr.* += 1;  // modify in place
}

// Const iteration
var it = list.constIterator(0);
while (it.next()) |ptr| {
    std.debug.print("{}\n", .{ptr.*});
}

// Bidirectional
while (it.prev()) |ptr| {
    // ...
}

// Peek without advancing
if (it.peek()) |ptr| {
    // ...
}

// Jump to index
it.set(50);

Capacity Management

// Grow capacity
try list.growCapacity(allocator, 100);
try list.setCapacity(allocator, 100);  // grow or shrink

// Shrink
list.shrinkCapacity(allocator, 50);  // may fail silently
list.shrinkRetainingCapacity(new_len);

// Clear
list.clearRetainingCapacity();
list.clearAndFree(allocator);

Copy to Contiguous Slice

var dest: [100]i32 = undefined;
list.writeToSlice(&dest, 0);  // copy from index 0

// Copy subset
list.writeToSlice(dest[50..], 50);  // copy starting at index 50

Memory Layout

Segments grow exponentially:

prealloc=0:  shelf 0: 1 element
             shelf 1: 2 elements
             shelf 2: 4 elements
             ...

prealloc=4:  prealloc: 4 elements (inline)
             shelf 0: 8 elements
             shelf 1: 16 elements
             ...

Common Pattern: Object Pool with Stable References

const Object = struct {
    data: [1024]u8,
    next: ?*Object,
};

var pool = std.SegmentedList(Object, 64){};

// Create objects - pointers remain valid
const obj1 = try pool.addOne(allocator);
const obj2 = try pool.addOne(allocator);
obj1.next = obj2;  // safe: obj2 pointer won't change