Introduce a notion of execution context to Rust.
This is primarily motivated by an issue with the
Alloc trait that has been discussed in Tracking issue for custom allocators in standard collections (and possibly elsewhere, although that’s all I could find).
In the current
dealloc is a method. This means that if you want to create a type which takes an
A: Alloc type parameter, and you want to have some of your code call
dealloc, you need to store an instance of
A (or a reference to
A) internally. For the global allocator,
A is a ZST, so this doens’t matter. For non-global allocators, however,
A or a reference to it is usually going to be at least a machine word in size.
For types that store large objects (e.g.,
Vec), this isn’t a big deal. For types which store small objects, however, this is a huge deal.
Box<T, A: Alloc>, for example, doubles in size at a minimum from storing a pointer to
T to storing a pointer to
T and a reference to
A. If you’re implementing something like a linked list or a tree, this represents a near-doubling of the size of your data structure.
In my mind, this points to a broader point about Rust: Rust’s story that resource cleanup always happens safely via ownership +
drop implicitly relies on ambient authority. For example,
Box<T> can only dealloc its contents on
drop because the global heap is ambient authority. When resource cleanup needs to rely on non-ambient state, we’re left with no good options. Storing an
A: Alloc is such a case.
My proposal is to introduce a notion of “execution context” to Rust. It would allow ambient authority to be replaced by context-scoped state. An execution context would be:
- Set by a caller
- In place for a function call (or a scope within a function call) and all of its sub-calls
- Any code could query for the current execution context
- Any code could set a sub-context which would override the existing context for the duration of some smaller scope, similar to variable shadowing
It would be very similar to Go’s
context, Scheme’s parameters, or Haskell’s
Reader monad. In the case of an allocator, if a custom (non-heap) allocator were used (for example by a particular data structure), code (for example, methods on that data structure) would set the allocator as an element of the execution context.
drop implementations - crucially, including
Box - would have access to the allocator in order to perform deallocation without needing to store it inside themselves.
There is none. I don’t actually know how we’d implement this - I just want to prompt discussion.
Some important questions include:
- Would this need explicit language support?
- How can we ensure that this works in
I’m especially interested to hear feedback on:
- Would this actually solve these sorts of ambient authority problems, or are there holes in this idea that folks notice?
- Are there other good examples (besides custom allocators) of Rust code that would benefit from such a system?