it sounds like implicitly passing the allocator as an extra parameter in every call would solve the problems you identify. if it's passed in a call-preserved register, it doesn't even make that implicit passing slower, because it requires zero instructions to not change the register before calling a subroutine
(when i've done similar things, it's been to pass an arena for inlined pointer-bumping allocation, a strategy widely used by runtimes that want to make allocation fast. normally this requires two registers, though chicken gets by with just one)
However, again, if you don't care about your allocators, why are you using a systems programming language? If you're willing to give up control over allocation, you are way, way better off in a managed memory (garbage collected or reference counted) language.
Systems programming is pain for gain. You give up a lot of convenience in order to have strict control over things--control of latency, control of memory, control of threading, etc. The price for that control is programming with a lot fewer abstractions and far less help from the language/compiler/interpreter to keep you from shooting yourself in the foot.
If you aren't using that "control" then you're just making life painful for yourself for no good reason.
Obviously, people can use whatever language they perfectly well want for whatever reason they want. Given how much I talk about and use C, Rust and Zig, people are always surprised that if they ask if they should use any of the systems languages (C/C++/Rust/Zig/etc.) my first response is always "Oh, hell, no."
passing an allocator as an implicit parameter doesn't give up any control over allocation; with that mechanism you can still pass a different allocator when you want (and i assume odin allows that, though i haven't tried it)
but i disagree pretty comprehensively with your comment. i don't agree that systems programming is pain, i don't agree with your definition of systems programming as programming with tight low-level control, i don't agree that tight low-level control is pain, i don't agree that abstraction is a necessary or useful thing to give up either for systems programming or to get tight low-level control (though it certainly is expedient), i don't agree that garbage collection (including reference counting) is the only way to simplify memory management, and i don't agree that either systems programming or tight low-level control requires accident-prone languages (and i'm especially surprised to see that assertion coming from an apparent rustacean)
that is, i recognize that these tradeoffs are possible. i just disagree that they're necessary
> that is, i recognize that these tradeoffs are possible. i just disagree that they're necessary
That's a nice theoretical position; however, the current programming languages as they exist disagree with you.
I would also point out the one of the problems in "systems programming" is that it encompass both "can run full blown Linux" and "slightly more CPU than a potato and has no RAM". Consequently, there are VERY different lenses looking at "systems programming".
> i don't agree that either systems programming or tight low-level control requires accident-prone languages (and i'm especially surprised to see that assertion coming from an apparent rustacean)
Rust is particularly poor when you can't define ownership at compile time. If you have something that you init once and then make read-only, you will be writing unsafe. If memory ownership passes between Rust and something else (say: memory between CPU and graphics card), you will be writing lots of unsafe. RPC via shared memory with a non-Rust process--prepare for pain.
Writing "unsafe Rust" is super difficult--moreso that straight C/C++. If you are writing enough of it, why are you in Rust?
You have to architect your solution around Rust to make the most of it and lots of things (especially stuff at runtime) are off limits. See: Cliff Biffle from Oxide and all the things he needed to do to make their RTOS completely defined at compile time because anything at runtime just gave Rust fits.
> because anything at runtime just gave Rust fits.
This is not the main reason that hubris does things up front. It does that because it makes for a significantly more robust system design. From the docs:
> We have chosen fixed system-level resource allocation rather than dynamic, because doing dynamic properly in a real-time system is hard. Yes, we are aware of work done in capability-based memory accounting, space banks, and the like.
> That's a nice theoretical position; however, the current programming languages as they exist disagree with you.
as i pointed out in another comment, c++ is a good example of having tight low-level control without programming with a lot fewer abstractions, and c++ is hardly a little-known language we can dismiss as irrelevant
so the current programming languages as they exist do not disagree with me
Not to mention we're talking about programming games here and you generally preload your assets and don't mess with them dynamically or your performance tanks. It's not only systems programming.
game engines typically do a lot of dynamic allocation, even aside from loading new levels; tight control over where that allocation happens and what to do when it fails is maybe the most important reason c++ is so popular in the space
c++ is a good example of having tight low-level control without programming with a lot fewer abstractions. indeed, the abundance of abstraction is what makes c++ usually so painful
i think it's reasonable to describe game engine programming as systems programming. i mean some of it, like writing interpreters, persistence frameworks, drivers for particular devices, and netcode, is pretty obviously right in the core of systems programming, but plausibly all of it is in that wheelhouse
> tight control over where that allocation happens and what to do when it fails is maybe the most important reason c++ is so popular in the space
It's also the most important reason why the C++ standard library is so unpopular in the space. All standard C++ containers allocate implicitly because often enough that's OK even in systems programming. What matters is that you can control the allocation more finely when you want.
yeah, agreed. when adding the stl to the standard library was being debated in the standards committee, microsoft forced the stl to use pluggable allocators, so you can easily make your std::vector allocate on your per-frame pointer-bumping heap, but often that's a poor substitute for not allocating at all
An allocation library cannot serve two masters. Maximizing single thread performance is anathema to multithreaded performance.
I'd go further. If allocators don't matter, why are you using a systems programming language in the first place?