Suppose I allocate a slice and fill it only partially, like
fn allocSlice(allocator: std.mem.Allocator, n_items: u8) ![]u8 {
var my_slice = try allocator.alloc(u8, 256);
for (0..n_items) |i| {
my_slice[i] = @truncate(i);
}
return my_slice[0..n_items];
}
Since I didn't use the allocated space between n_items
and 256
, will that leak? Will it remain allocated to that same slice (that was returned from the allocSlice
function) and be freed when that partial slice is freed?
Or should I free the unused space before returning?, like
allocator.free(my_slice[n_items..256]);
Turns out the approach above causes the program to crash with the following message:
panic: Invalid free
But also if I return the (partial) slice from the function and later try to free it I get a different error:
error(gpa): Allocation size 256 bytes does not match free size 12.
So it turns out neither of these approaches is correct. What is, then?
There's no such thing as "partially" freeing a slice. You either keep or free the whole thing.
You can try allocator.resize
, which depending on the implementation might be able to shrink the buffer in-place (implementations are allowed to refuse to do this however), or allocator.realloc
which will return a new slice with only the contents up to what you requested in it (or a larger slice with uninitialised memory at the end if you request a larger size), while invalidating the old slice.
Generally, if you know in advance how much memory you're going to need you should only request that much memory in the first place. resize
/realloc
are more useful for cases where how much memory you need can't be determined in advance (and there are already types in the standard library like ArrayList
which take care of the common cases of this occurring, so you usually don't have to write that kind of code from scratch unless you're writing low level IO or data structure code).