ArrayList.initBuffer: Using Allocator-accepting functions not necessarily illegal behavior?

The std.array_list.Aligned.initBuffer(buffer) function is currently documented like this (emphasis mine):

Initialize with externally-managed memory. The buffer determines the capacity, and the length is set to zero.

When initialized this way, all functions that accept an Allocator argument cause illegal behavior.

This—at least with the current implementation—seems like a gross oversimplification. Yes, if buffer is a slice pointing to an array on the stack or inside of a struct/union, that does indeed cause illegal behavior, but if buffer simply points to memory gotten via e.g. std.mem.Allocator.alloc(T, n), this seems to be completely fine, legal, and even useful to me.

In fact, I do have a use case in my connect-4-style game, where I create an ArrayList of potentially-winning points on the board during each turn, and—to avoid re-allocating the list over and over for each turn—I stash the .allocatedSlice() when the turn-processing function is complete, and then reuse it in the next invocation.

Is this perhaps something that should be clarified in the docs? Or would it still be discouraged to use .initBuffer(buffer) for something like this, maybe because of some planned changes that I don’t yet know about?

For now this seems to just be an overly cautious warning. If you know what you are doing, and carefully read the std source code, then of course you can find legal ways to do this.

Why not just keep the entire ArrayList then with calls to clearRetainingCapacity() at the end?
It’s only 8 more bytes to store, but your intent is much clearer to understand, than using a function in a seemingly (according to the docs) unsafe way.

You know, that is a valid point. I may have overthought it :sweat_smile:

fromOwnedSlice is intended for that use case, although the semantics are different (fromOwnedSlice assumes the entire slice is “populated” (.items = slice), whereas initBuffer assumes none of the slice is “populated” (.items = buffer[0..0])).

Another way to go would just be to initialize directly when you have something in particular in mind:

my_list_that_i_know_how_to_init = .{
    .items = my_alloced_slice[0..populated_len],
    .capacity = my_alloced_slice.len,
};

The combination of .toOwnedSlice and .fromOwnedSlice was actually what I had used originally before a cleanup, then changed to .allocatedSlice and what is the implementation of .initBuffer (but I haven’t bothered to look at that point). Felt better because of the removed reallocation.

But yeah, i’m probably just producing a lot of ado about something pretty trivial over here :person_shrugging: