Description
Currently there's only Allocator
trait that provides both allocations and deallocations.
And Box
, Vec
and other types has A: Allocator
generic parameter.
However there are allocators with no-op deallocations and thus do not require collections and smart-pointers to keep any allocator state to deallocate.
It would reduce size and improve performance somewhat significantly if Box<T, A>
would be able to use ZST A
parameter if no state is required for deallocation and allocation is not needed.
I propose the following solution:
-
Split
Allocator
trait into two -Deallocator
andAllocator
.
They can be defined as following.unsafe trait Deallocator { fn deallocate(&self, ptr: NonNull<u8>, layout: Layout); } unsafe trait Allocator: Deallocator { /* all other methods */ }
-
Define that deallocator
deallocator: D
created using<D as From<A>>::from(alloc)
may deallocate memory allocated byalloc
, any of its copy and equivalent allocators. -
Leave only
A: Deallocator
bound on collections and smart-pointers and all their impl blocks where allocation is not performed. -
Implement
From<Box<T, A>> for Box<T, D> where D: From<A>
and similar impls for other types with allocator type.
This impl may conflict with others. The alternative is to add a method.
After this is done then allocators with possible no-op deallocation (like bumpalo::Bump
or blink_alloc::BlinkAlloc
) may define ZST deallocator type that does nothing on deallocation and only provides a lifetime to ensure that allocator is not reset.
On the bumpalo
as example
struct Bumped<'a> {
_marker: PhantomData<&'a Bump>,
}
unsafe impl<'a> Deallocator for Bumped<'a> {
fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {}
}
impl<'a> From<&'a Bump> for Bumped<'a> {
fn from(_bump: &'a Bump) -> Self {
Bumped { _marker: PhantomData }
}
}
// Usage
fn foo<'a>(bump: &'a Bump) {
let box: Box<u32, &'a Bump> = Box::new_in(42, bump);
let box: Box<u32, Bumped<'a>> = box.into();
assert_eq!(size_of_val(&box), size_of::<usize>());
// Following doesn't compile as cloning `Box` requires `A: Allocator`
// let box2: Box<u32, _> = box.clone();
// If such ability is required - do not downgrade allocator to deallocator.
}