zig-skills/references/std-arraylist.md

178 lines
5.1 KiB
Markdown

# std.ArrayList (Zig 0.16.0)
Dynamic array (vector) that grows as needed.
**Note:** `std.ArrayListUnmanaged` is deprecated - use `std.ArrayList` (unmanaged-style API with allocator passed to methods).
## Initialization
```zig
// Use the supported explicit empty initializer.
var list: std.ArrayList(u32) = .empty;
defer list.deinit(allocator);
// With pre-allocated capacity
var reserved = try std.ArrayList(u32).initCapacity(allocator, 100);
defer reserved.deinit(allocator);
// From existing slice (takes ownership)
var list = std.ArrayList(u32).fromOwnedSlice(existing_slice);
// Fixed buffer: use bounded/no-allocator operations only. Passing an allocator
// to a method on an initBuffer list is illegal.
var buffer: [8]i32 = undefined;
var stack = std.ArrayList(i32).initBuffer(&buffer);
```
## Basic Operations
```zig
// Append
try list.append(allocator, 42);
try list.appendSlice(allocator, &[_]u32{1, 2, 3});
// Append without allocation (asserts capacity exists)
list.appendAssumeCapacity(42);
list.appendSliceAssumeCapacity(&[_]u32{1, 2, 3});
// Access items
const items = list.items; // []T slice
const first = list.items[0];
const last = list.getLast(); // returns T; asserts if empty
const maybe_last = list.getLastOrNull(); // returns ?T
const popped = list.pop(); // returns ?T, removes last
// Insert at index
try list.insert(allocator, 2, value);
try list.insertSlice(allocator, 2, slice);
// Remove
const removed_ordered = list.orderedRemove(index); // O(n), preserves order
const removed_swapped = list.swapRemove(index); // O(1), changes order
```
## Capacity Management
```zig
// Ensure space for N more items
try list.ensureUnusedCapacity(allocator, 10);
// Ensure total capacity is at least N
try list.ensureTotalCapacity(allocator, 100);
// Attempt to shrink capacity. If resizing runs out of memory, the list keeps
// excess capacity while still reducing its logical length.
list.shrinkAndFree(allocator, list.items.len);
// Clear
list.clearRetainingCapacity(); // keeps memory
list.clearAndFree(allocator); // frees memory
```
## Ownership Transfer
```zig
// Get owned slice (empties list, caller owns memory)
const owned = try list.toOwnedSlice(allocator);
defer allocator.free(owned);
// Get null-terminated slice
const z_str = try list.toOwnedSliceSentinel(allocator, 0);
defer allocator.free(z_str);
```
## Iteration
```zig
for (list.items) |item| {
// read-only
}
for (list.items) |*item| {
item.* += 1; // modify in place
}
for (list.items, 0..) |item, i| {
// with index
}
```
## Common Patterns
```zig
// Collect from iterator
var list: std.ArrayList(u8) = .empty;
for (some_iterator) |item| {
try list.append(allocator, item);
}
// Build string
var buf: std.ArrayList(u8) = .empty;
try buf.appendSlice(allocator, "Hello ");
try buf.appendSlice(allocator, name);
const result = try buf.toOwnedSlice(allocator);
// Remove while iterating (iterate backwards)
var i: usize = list.items.len;
while (i > 0) {
i -= 1;
if (shouldRemove(list.items[i])) {
_ = list.swapRemove(i);
}
}
```
## Reserve-First Pattern (Exception Safety)
When inserting into multiple containers or when partial mutation would corrupt state, use **reserve-first**: separate fallible reservation from infallible mutation.
```zig
// BAD - partial failure leaves invalid state
fn addItem(list: *std.ArrayList(u32), map: *std.AutoHashMapUnmanaged(u32, usize), gpa: Allocator, value: u32) !void {
try list.append(gpa, value); // Can fail
try map.put(gpa, value, list.items.len - 1); // If this fails, list has orphan entry!
}
// GOOD - reserve first, then mutate
fn addItem(list: *std.ArrayList(u32), map: *std.AutoHashMapUnmanaged(u32, usize), gpa: Allocator, value: u32) !void {
if (map.contains(value)) return error.DuplicateItem;
const index = list.items.len;
// Phase 1: Reserve. Logical contents are unchanged, but capacity may grow
// and existing element pointers may be invalidated.
try list.ensureUnusedCapacity(gpa, 1);
try map.ensureUnusedCapacity(gpa, 1);
errdefer comptime unreachable; // Phase 2: No errors after this point
// Phase 3: Mutate (infallible)
list.appendAssumeCapacity(value);
map.putAssumeCapacityNoClobber(value, index);
}
```
**Key methods:**
- `ensureUnusedCapacity(gpa, n)` - Reserve space for n more items. It can fail
and can reallocate, but it does not append or remove logical elements.
- `appendAssumeCapacity(item)` - Append without allocation (cannot fail, asserts capacity)
- `appendSliceAssumeCapacity(items)` - Append slice without allocation
See **[Reserve-First Exception Safety](patterns.md#reserve-first-exception-safety)** for detailed explanation and real-world examples.
## BoundedArray Replacement
`std.BoundedArray` was REMOVED in 0.15.x. Use `initBuffer` instead:
```zig
// OLD (removed)
var arr = std.BoundedArray(u8, 64){};
// NEW
var buffer: [64]u8 = undefined;
var arr = std.ArrayList(u8).initBuffer(&buffer);
// Bounded operations report capacity exhaustion.
try arr.appendBounded(value); // error.OutOfMemory if full
// Assume-capacity operations assert; allocator-taking operations are illegal
// for an initBuffer list.
```