Use case: I’m generating MIP levels of a texture. Type hierarchy:
const Texture = struct {
levels: []Level,
};
const Level = struct {
width: u16,
height: u16,
pixels: []Color,
};
const Color = struct {
r: u8,
g: u8,
b: u8,
};
I can allocate levels in my init:
const levels = try allocator.alloc(Level, n_levels);
errdefer allocator.free(levels);
But how do I allocate levels themselves such that everything gets freed if I fail in the middle of the process?
for (0..n_levels) |i| {
const level_size = some.math();
levels[i] = try allocator.alloc(Color, level_size);
// cannot errdefer here, will go out of scope before doing anything
populate(levels[i]);
}
One workaround that comes to mind is
const levels = try allocator.alloc(Level, n_levels); // returns uninitialized
// initialize each item such that it is safe to free
for (levels) |*level| {
level.* = .{
.width = 0,
.height = 0,
.pixels = &.{},
};
}
errdefer freeLevels(allocator, levels);
...
fn freeLevels(allocator: std.mem.Allocator, levels: []Level) {
for (levels) |level| {
allocator.free(level.pixels);
}
allocator.free(levels);
}
It seems like this should work, but that’s a lot of boilerplate which only becomes more convoluted with depth. Is there a better way?