1. 76
  1. 35

    I have never used Zig and this will affect me in absolutely no way.

    But this is such a marvelous piece of technical writing that my immediate reaction was to try to read more from the author, only to be saddened to learn that this is the only thing that they have published.

    1. 6

      Agreed, totally. This is just really beautifully lucid writing.

    2. 10

      Ugh. I was already unhappy about how Zig programs have to pass an allocator reference all over the place, adding it to function signatures and object fields. But now that reference is going to be twice as big, i.e. taking up two ABI registers in calls and making all those structs 8 bytes bigger. Just to save a few cycles dereferencing it.

      1. 23

        Really. The everything-that-allocates-passes-an-allocator thing is one of my favorite pieces of Zig’s design. I like the fine-grained control it gives and it makes manual memory management pleasant.

        1. 8

          Passing an allocator is annoying until you want code that doesn’t allocate. This approach is building a bright future for resource-constrained (bare-metal) platforms. It becomes possible to identify allocating parts of code, and people will probably write much more allocationless libraries.

          1. 8

            And also opens up fine-grained control of when things are allocated/deallocated by allowing a selection of allocators whenever necessary.

            Use a limited bump/arena allocator per API request and free everything at the end. Use a totally different allocator on the backend. Use the C allocator when passing pointers to C functions. It’s all good.

            1. 1

              It’s all good.

              Unless I am missing something, with this design all allocations in Zig incur an overhead of a virtual function call . With this change, there is a better chance of devirtualuzation but AFAICS no guarantees of any kind. While this is probably not a big deal if your allocator ends up calling malloc(), for something like a stack-based arena this can be a significant overhead.

              Contrast this to C++, where an allocator type can be (and is for all the std containers) a template parameter, which means your allocator calls can actually be inlined.

              1. 1

                overhead of a virtual function call

                Branch prediction is a funny thing. Here are two functions; both do a lot of work and perform a lot of calls. One performs those calls directly, and one indirectly. On my machine, they take the same amount of time to execute (amd 3960x, about 0.6 seconds).

                (Of course that is a microbenchmark and not representative; but I think the point is illustrative. E.G. jump to a thunk for your allocations and you won’t be so likely to blow out your btb. In the limit, use an inline cache.)

          2. 5

            It incentivise building abstractions that don’t allocate in perf critical calls.

          3. 6

            It’s interesting comparing this approach to C++. In C++, you’d generally write code that parameterizes over the allocator at compile time using a template parameter. This gives you the flexibility of libraries that work with many different allocators, but without any runtime overhead, since the dispatch is selected at compile time.

            I’m interested in Zig, but haven’t really dug into it. Anyone know why they chose a runtime approach?

            1. 3

              In C++, you’d generally write code that parameterizes over the allocator at compile time using a template parameter.

              I haven’t written C++ in over 20 years, so forgive my ignorance here: does this apply to the STL too?

              One of the big ergonomic points for Zig is that you know when allocation is happening because you passed an allocator to a function, and everything that can fail allocation returns an error that must be dealt with at the return site, which has advantages (and disadvantages) over exceptions on out of memory errors.

              1. 3

                It does. It’s usually an optional type parameter with a default.

                1. 3

                  Could this ergonomics/functionality also be implemented in Zig via compile-time monomorphisation over allocators?

                  1. 3

                    It does, and it’s why people who want custom allocators don’t use the STL.

                    If you pass it as a template arg you can only have one of each kind of allocator, so you can’t really do anything beyond a malloc/free wrapper. It also means any code using such data structures has to be a template too and that brings its own problems.

                    edit: I guess template <typename Allocator> f(Allocator* a); works, but making everything templates means you have to put everything in headers and then those headers have to include more headers and it becomes a big mess. All for something which is unlikely to ever matter in the end and can be worked around if it does.

                2. 4

                  Breaking changes like this, should be, as it seems this one is, safe (compiler should help warn if not done), documented (here), and rare (not every release, and not many things each time it happens).

                  Seems like this should be an easier fix for people than even python 3.8 to 3.9!

                  1. 15

                    I’d put it differently:

                    • before stability is promised, such breaking changes should be as frequent as necessary
                    • after stability is promised, such breaking changes should not happen at all.
                    1. 5

                      For a language pre 1.0 ? I used rust right before 1.0 and had to work around broken nightly builds, and that’s totally ok. I was always allowed to go back to an older nightly and fix stuff when I’ve got the time. And zig is the same. That’s why there is this big warning on the website that it’s very functional but not in 1.0 condition. And you’d bet some of the rust core people would love to have that freedom to change false assumptions that are now warts in rust stable. So in summary I wouldn’t worry about that or blame zig, not before 1.0

                      1. 2

                        Meanwhile zig here is making a breaking change as responsibly as possible, not breaking code every few months.