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 and Allocator.
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 by alloc, 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.
}
Currently there's only
Allocatortrait that provides both allocations and deallocations.And
Box,Vecand other types hasA: Allocatorgeneric 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 ZSTAparameter if no state is required for deallocation and allocation is not needed.I propose the following solution:
Split
Allocatortrait into two -DeallocatorandAllocator.They can be defined as following.
Define that deallocator
deallocator: Dcreated using<D as From<A>>::from(alloc)may deallocate memory allocated byalloc, any of its copy and equivalent allocators.Leave only
A: Deallocatorbound 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::Bumporblink_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
bumpaloas example