BoundedArray vs open-coding

I often need to process a constant amount of things in a single function. It feels that what I should be doing is using a bounded array:

var items: std.BoundedArray(Item, 16) = .{};

However, I find BoundedArray API to be quite verbose, and often end up open coding it on the spot:

var items: [16]Item = undefined;
var item_count: usize = 0;

Which is considered more idiomatic Zig? How can I to stop avoiding and love the BoundedArray?

4 Likes

Don’t you think that making an ad-hoc BoundedArray results in even more verbosity? Certain things that would be an API call turn into multiple lines.
Also, using the same BoundedArray in multiple functions leads to code reuse, with smaller binaries and better cache locality.
Slightly tangential, but I remember seeing in a PR a comment from Andrew saying that the Zig compiler stopped using BoundedArray in favor of ArrayList, because it resulted in better code generation. Anyone could elaborate on this?

I used to have a lot of small arrays with counters as well, but nowadays I’ve almost exclusively switched to allocation and array lists. Of course you need a good (and thread local) allocator for this, but the big advantage for me is that I don’t have to worry about setting the upper limit right.

As for code generation of BoundedArray, Zig has some trouble with arrays in structs, and in certain situations it will just copy the entire array when accessing a single element. Here is the relevant issue: Another array access performance issue. · Issue #13938 · ziglang/zig · GitHub

1 Like

When you say “constant”, I hear known lower and upper bound which are the same number. If that’s correct, I would use an array personally, there seems to be no downside.

BoundedArray is more for a known upper bound, but where the actual number of elements is runtime-determined. I’ve never had occasion to use one actually, it seems like the idea is that it can use the familiar ArrayList methods but with a stack allocation of constant size?

I’m sure that can be useful, but if I need to store exactly sixteen of something I use a plain-old-array and a separate index. Might be force of habit from other languages but it works.

If you like the ArrayList API better and know you will not add more items than the bounds of the buffer allows, then you can do:

var items: [16]Item = undefined;
var list: std.ArrayListUnmanaged(Item) = .initBuffer(&items);

// ...
// note: do *not* call any ArrayListUnmanaged functions that take an allocator

The original intention with initBuffer was to replace BoundedArray entirely (the PR that added it originally had the title std: remove BoundedArray), but BoundedArray was ultimately kept because it can be used for different use cases (e.g. it allows safe copying).

One thing that would make using initBuffer nicer would be std.array_list: Add *NoResize functions to ArrayListUnmanaged by notcancername · Pull Request #18361 · ziglang/zig · GitHub, which is on my list of PRs to revive at some point.

5 Likes