I guess I could still use a FixedBufferAllocator in those cases. Is that the way to go? Why not have a much more simple initNoAlloc function? Because it’s boilerplate to always provide both?
And what should I usually do with my own data structures, in particular if I want to model certain mathematical (numerical) objects?
That said, LinearFifo (a kind of ring buffer, as mentioned in that issue) sort of have the flexibility you ask for. Not sure if LinearFifo will survive in std, but your design consideration is certainly valid.
Accepting an allocator allows for both, e.g. you can pass in a FixedBufferAllocator backed by a stack buffer. Sure there will be some overhead, but it’s usually negligible and in my opinion it’s more important to have a simpler API.
You don’t necessarily have to choose between the two; you can also design your data structure in such a way that it supports both usages. Look to std.ArrayListUnmanaged for inspiration:
// allocator-backed
{
const allocator = std.heap.page_allocator;
var items: std.ArrayListUnmanaged(i32) = try .initCapacity(allocator, 256);
defer items.deinit(allocator);
items.appendAssumeCapacity(1);
items.appendAssumeCapacity(2);
items.appendAssumeCapacity(3);
}
// buffer-backed (calling any method that takes an allocator argument becomes UB)
{
var buf: [256]i32 = undefined;
var items: std.ArrayListUnmanaged(i32) = .initBuffer(&buf);
items.appendAssumeCapacity(1);
items.appendAssumeCapacity(2);
items.appendAssumeCapacity(3);
}
Yeah, I feel like it’s a weighing of pros and cons, and I wonder if maybe there was some “usual” way of how this is handled.
So it looks like std goes both ways and isn’t always consistent: std.RingBuffer always demands allocation (disregarding whether it will disappear one day), but std.ArrayListUnmanaged provides two init functions (initBuffer and initCapacity).
(I do understand that making std consistent isn’t a top priority as of yet, but to be done some time in future.)