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;
...
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 }