Search code examples
zig

How to idiomatically return an accumulated slice/array from a Zig function


See the following code

const std = @import("std");
const print = std.debug.print;

test "Demo accumulation" {
    const accumulated = try accumulate();
    print("DEBUG - accumulated values are {any}\n", .{accumulated});
}

fn accumulate() ![]u32 {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var list = std.ArrayList(u32).init(allocator);
    // defer list.deinit(); <-- this is the problem line
    try list.append(1);
    try list.append(2);
    try list.append(3);
    return list.items;
}

If the problem-line is commented-out, the compiler (correctly) warns me of a memory leak; but if it's kept in, then referencing accumulated gives a segmentation fault. Is it possible in Zig to avoid either bad consequence while continuing to return a []u32 (as opposed to, say, returning the ArrayList and deinit()ing it in the context of the calling function?)

In particular, the following still gives a memory leak:

...
    var list = std.ArrayList(u32).init(allocator);
    defer list.deinit();
    try list.append(1);
    try list.append(2);
    try list.append(3);

    const response = try allocator.alloc(u32, list.items.len);
    @memcpy(response, list.items);
    return response;
...

Solution

  • Idiomatically, the function would take an allocator. It can return a slice or the ArrayList itself, depending on the need.

    test "Demo accumulation" {
        const allocator = std.testing.allocator;
        const accumulated = try accumulate(allocator);
        defer allocator.free(accumulated);
        print("DEBUG - accumulated values are {any}\n", .{accumulated});
    }
    
    fn accumulate(allocator: std.mem.Allocator) ![]u32 {
        var list = std.ArrayList(u32).init(allocator);
        errdefer list.deinit();
        try list.append(1);
        try list.append(2);
        try list.append(3);
        return list.toOwnedSlice();
    }
    

    This prints:

    $ zig build test
    test
    └─ run test stderr
    DEBUG - accumulated values are { 1, 2, 3 }