5 minute read

There are… several things I find wanting about the current version of the core::alloc::Allocator trait (as of 2023-08-06). I’m going to do a short series where I post about some of the bigger issues, and explore the different tradeoffs and design choices we might make. This post is an introduction to that.

So, I don’t think allocator is ready to stabilize, but I’ve never written down all my issues with it. This is me doing that (somewhat), and also announcing my intent to do some work on it’s design.

Obviously the API isn’t totally broken, but my feeling is that there’s a few fundamental issues (things which we either should change or be very certain this is the choice we want to make), and then a (very) long tail of smaller warts and highly dubious tradeoffs. A lot of this is just down to history: the current API has grown mostly organically, and aspects of it have have been changed without considering how that change impacts other aspects of the API. There’s also a few bits which just… didn’t end up working for their intended purpose (for reasons that might have been hard to see in advance), but that purpose is so niche that nobody particuarly cares.

The long and short of it is I think the trait needs a bit of an overhaul, or at least a thorough tune-up that considers things in a wholistic manner. Allocation is such a core part of systems programming that I’d really prefer we don’t half-ass it. I think this probably will end up being an RFC (but if I find a way to justify not writing an RFC, that’d definitely be better for my mental state…)

What are some of the problems?

So this is tricky, since my biggest complaints are hard to describe in only a few sentences. That said, here’s a shotgun list of open questions I have around this stuff, that probably covers the most important things:

  1. Should &mut self be used instead of &self? I believe this loses no flexibility (think about how Read taking &mut doesn’t force overhead on file IO, because we impl std::io::Read for &File), and without this, interior mutability must be used in nearly all non-ZST allocators, which breaks Send/Sync.
  2. Should Layout be NonZeroLayout? I’ve written about this in the issue, and think the answer is likely “yes”, but that other avenues should be explored (for example, perhaps an approach like core::iter::{FusedIterator, Fuse} would be enough).
  3. Should grow take a length of bytes that indicates how much is worth copying? If we provide a separate in-place growth API (which would fail if it would have to copy), would that remove the need for this?
  4. The current way allocation excess (e.g. the length of NonNull<[u8]>) is exposed in the API has many issues, and in many situations an allocator which provides it would have worse performance than one which does not (often for surprising reasons).
  5. Should allocation excess include alignment information?
  6. Offset/Header-aligned allocation APIs (APIs like _aligned_offset_malloc, mi_malloc_aligned_at, and so on) are more efficient when the alignment is high and a less-aligned header is needed, should we support this?
  7. Related: can we do anything about the the combinatoric explosion of options without causing API stability issues?
  8. Rust code is unable to reuse allocations in many cases where it should be possible. (I’ve written on this blog for this reason before, but I do not really agree with my opinions from that time), how can we improve that?
  9. What to do about std::alloc::System? It’s not guaranteed to be compatible with direct use of the system allocation API, and may not provide the same guarantees (with regards to alignment) as the system one.
  10. Should we do anything about allocator polymorphism (dyn Allocator, C++’s PMR stuff, …)?
  11. What to do about GlobalAlloc (big topic)?
  12. Several more… I think these are the big ones. That said, feel free to reach out to me1 if you have a pet complaint that you think is missing here.

This isn’t really intended to be a list of blockers or anything, and I definitely am not suggesting I’ll write a blog post about all of them (So far I have 2 posts planned/started, one on &mut self and one on excess/reuse). I expect this to change a lot as I work through things too.

Why bother posting this? Why not just use github/zulip/irlo?

Well, I’ll of course be doing that too.

This post specifically is mostly to announce that I’m planning on working through some of the problems, and to bring attention to some of them (given that the working group is stalled and the github issue has long entered the state of “too many comments to be remotely usable”, this seems somewhat beneficial). I’ll be filing github issues too, or at least threads on Zulip for discussion.

Not to mention, a lot of what I’m doing this is to organize my own thoughts on the topic at hand. I don’t really have solutions in mind for most of the problems above, and it’s very likely that I’ll go into some of these and come out with “this is probably better than the alternatives”, which isn’t exactly something I’d want to file a github issue about.

P.S. I’ve complained a lot about allocation in Rust in the past (even on this blog), my opinions arount the topic have changed a lot as I’ve used Allocator in more situations2. Don’t believe anything I wrote more than, IDK, a year ago.

Addendum: What about the Store API?

There’s already an open RFC for an alternative to Allocator known as the Store API, so I get this question a lot when I’m griping about Allocator.

The fact that my posts will be about Allocator and not Store should hint at how I feel3 about these two approaches… but really, most of my biggest concerns about Allocator will apply to Store too, and most of the fixes would apply equally to Store as to Allocator.

This is because the part of the Store API which actually deals with allocation is (or at least seems to be) largely based on the Allocator trait4. This means that most of what I want to discuss about Allocator is probably still relevant to Store (and Allocator is much simpler to talk about).

To answer how I actually feel about it, I’d need another post (which I might do), and more importantly: to spend time actually using it – most of the issues I have with Allocator I only realized by using it, and I’ve never touched Store.

  1. chiovolonit@gmail.com by email, zuurr on Discord, or DM “Thom Chiovoloni” on rust-lang Zulip. 

  2. Implementing Allocator for allocators which were neither global nor bindings to C APIs (e.g. pools/arenas/etc implemented in Rust) has changed my opinion on things in this space in various ways. 

  3. My gut feeling is that it’s too complex to be the fundamental allocation building block of Rust. 

  4. Which makes sense, since Allocator has at least been used mostly successfully in the stdlib.