Hi everyone, I came across this memory leak and I can’t figure how to fix. the code is the following:
const std = @import("std");
const print = std.debug.print;
const ArrayList = std.ArrayList;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
defer std.debug.assert(gpa.deinit() == .ok);
var final = ArrayList(u8).init(allocator);
defer final.deinit();
var tmp = ArrayList(u8).init(allocator);
try tmp.appendSlice("First slice pushed");
try final.appendSlice(try tmp.toOwnedSlice());
print("Final: {s}\n", .{final.items});
}
When I print items of final
array list, I see First slice pushed
so I expect that the memory is now owned but the final
array list. But calling deinit()
on final results on a memory leak regarding the first slice pushed to tmp
:
error(gpa): memory address 0x1fcd9670000 leaked:
C:\SC\Zig\zig-windows-x86_64-0.14.0-dev.224+95d9292a7\lib\std\array_list.zig:457:67: 0x516476 in ensureTotalCapacityPrecise (array_list_leak.exe.obj)
const new_memory = try self.allocator.alignedAlloc(T, alignment, new_capacity);
^
C:\SC\Zig\zig-windows-x86_64-0.14.0-dev.224+95d9292a7\lib\std\array_list.zig:434:51: 0x507f62 in ensureTotalCapacity (array_list_leak.exe.obj)
return self.ensureTotalCapacityPrecise(better_capacity);
^
C:\SC\Zig\zig-windows-x86_64-0.14.0-dev.224+95d9292a7\lib\std\array_list.zig:468:44: 0x4d41ed in ensureUnusedCapacity (array_list_leak.exe.obj)
return self.ensureTotalCapacity(try addOrOom(self.items.len, additional_count));
^
C:\SC\Zig\zig-windows-x86_64-0.14.0-dev.224+95d9292a7\lib\std\array_list.zig:305:42: 0x4d160b in appendSlice (array_list_leak.exe.obj)
try self.ensureUnusedCapacity(items.len);
^
D:\mvachero\Documents\TaffDivers\Zig\Tests\dag\array_list_leak.zig:15:24: 0x4d13b0 in main (array_list_leak.exe.obj)
try tmp.appendSlice("First slice pushed");
As a workaround, I use the writer
interface of ArrayList
and deinit()
both lists:
const std = @import("std");
const print = std.debug.print;
const ArrayList = std.ArrayList;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
defer std.debug.assert(gpa.deinit() == .ok);
var final = ArrayList(u8).init(allocator);
defer final.deinit();
var tmp = ArrayList(u8).init(allocator);
defer tmp.deinit();
try tmp.appendSlice("First slice pushed");
var writer = final.writer();
try writer.print("{s}", .{tmp.items});
print("Final: {s}\n", .{final.items});
}
The doc says that when using toOwnedSlice()
there is no need to call deinit()
and indeed, it doesn’t change anything when doing so.
How does the ownership is transferred? How to manage such memory?
Thanks!