std.ArrayList
std.ArrayList is a growable array.
Use it when the number of elements is not known at compile time.
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var list = std.ArrayList(u8).init(allocator);
defer list.deinit();
try list.append(10);
try list.append(20);
try list.append(30);
std.debug.print("{any}\n", .{list.items});
}The output is:
{ 10, 20, 30 }An ArrayList(T) stores values of type T.
std.ArrayList(u8)This means: a growable array of bytes.
The list owns its memory. It uses the allocator passed to init.
var list = std.ArrayList(u8).init(allocator);When the list is no longer needed, release its memory.
defer list.deinit();Appending may allocate memory.
For that reason, append can fail.
try list.append(10);The try passes the error back to the caller if allocation fails.
Because of this, main returns an error union:
pub fn main() !voidThe !void return type means the function either succeeds with no value or returns an error.
The elements are accessed through the items field.
list.itemsThis field is a slice.
[]u8You can loop over it like any other slice.
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var list = std.ArrayList(i32).init(allocator);
defer list.deinit();
try list.append(3);
try list.append(5);
try list.append(8);
for (list.items) |value| {
std.debug.print("{d}\n", .{value});
}
}The output is:
3
5
8ArrayList has two important sizes.
The first is the number of elements in use:
list.items.lenThe second is the amount of storage reserved internally.
This storage is called capacity.
When there is unused capacity, appending can add an element without allocating.
When the capacity is full, the list allocates a larger buffer and copies the old elements.
You can reserve capacity in advance.
try list.ensureTotalCapacity(100);This is useful when the final size is roughly known.
An ArrayList can also be initialized with existing capacity.
try list.ensureTotalCapacity(1024);Then many appends can happen without repeated allocation.
To remove all elements but keep the allocation, use:
list.clearRetainingCapacity();To remove all elements and free the allocation, use:
list.clearAndFree();The first form is useful when the list will be reused.
The second form is useful when the memory should be returned immediately.
A common pattern is to build a string.
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var buf = std.ArrayList(u8).init(allocator);
defer buf.deinit();
try buf.appendSlice("hello");
try buf.append(' ');
try buf.appendSlice("zig");
std.debug.print("{s}\n", .{buf.items});
}The output is:
hello zigappendSlice appends many elements.
try buf.appendSlice("hello");append appends one element.
try buf.append(' ');The list does not add a sentinel byte unless asked.
If C-style string data is needed, make that explicit.
For ordinary Zig string data, buf.items is enough.
ArrayList also supports ordered removal.
_ = list.orderedRemove(1);This removes the element at index 1 and shifts later elements left.
It preserves order.
There is also unordered removal.
_ = list.swapRemove(1);This replaces the removed element with the last element.
It does not preserve order, but it is faster.
Use orderedRemove when order matters.
Use swapRemove when order does not matter.
Here is a complete example.
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var list = std.ArrayList(i32).init(allocator);
defer list.deinit();
try list.appendSlice(&.{ 10, 20, 30, 40 });
_ = list.orderedRemove(1);
std.debug.print("{any}\n", .{list.items});
}The output is:
{ 10, 30, 40 }ArrayList is simple, but it makes ownership visible.
The allocator is explicit.
Allocation failure is explicit.
The list must be deinitialized.
The stored elements are exposed as a slice.
This makes it easy to pass the data to functions that do not care whether the slice came from an array, an allocation, or a growable list.
Exercise 14-9. Create an ArrayList(u32) and append the numbers 1 through 10.
Exercise 14-10. Build a string with ArrayList(u8) and appendSlice.
Exercise 14-11. Remove an element with orderedRemove.
Exercise 14-12. Repeat the removal with swapRemove and compare the result.