impl<A> Allocator for &mut A
Proposal
add an Allocator impl for &mut A when A: ?Sized + Allocator that forwards to the underlying implementation
Problem statement
&mut A currently doesn't impl Allocator when A does
Motivating examples or use cases
the current state of things makes it cumbersome to work with Send allocators (especially type erased dyn 'a + Send + Allocator). examples of allocators that are Send, but not Sync are local allocators that wrap a RefCell to satisfy the immutable ref api.
Solution sketch
we can just copy the &A implementation
unsafe impl<A> Allocator for &mut A
where
A: Allocator + ?Sized,
{
#[inline]
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
(**self).allocate(layout)
}
#[inline]
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
(**self).allocate_zeroed(layout)
}
#[inline]
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
// SAFETY: the safety contract must be upheld by the caller
unsafe { (**self).deallocate(ptr, layout) }
}
#[inline]
unsafe fn grow(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
// SAFETY: the safety contract must be upheld by the caller
unsafe { (**self).grow(ptr, old_layout, new_layout) }
}
#[inline]
unsafe fn grow_zeroed(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
// SAFETY: the safety contract must be upheld by the caller
unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
}
#[inline]
unsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
// SAFETY: the safety contract must be upheld by the caller
unsafe { (**self).shrink(ptr, old_layout, new_layout) }
}
}
Alternatives
an alternative solution is using a ByMut<A> wrapper that's a thin wrapper over &mut A, but this is an unnecessary hoop to make users jump through
Could you give a code example of when this is useful? All of Allocator's methods take &self so you should almost never need a &mut A.
type erased allocators are my main use-case
instead of making my type generic over the allocator, i want to make it hold a dyn Allocator + Send to avoid a big number of monomorphizations. given a Send + !Sync allocator (e.g., a bump allocator containing a Cell/UnsafeCell), &mut A is Send but not &A
That also means your type will hold exclusive access to that allocator, so you can't even clone, for example.
this is acceptable. my clone uses the global allocator, and clone_in takes an allocator parameter that can be used with explicit clones
I'm afraid I still don't understand why you would need a &mut A: a bump allocator which holds a reference to a memory pool with an UnsafeCell cannot (and should not) implement Send. A code example would help clarify what you are trying to describe.
it can implement Send, as long as it has an exclusive reference to the memory pool. since that means at most one thread can bump the pointer
this lets thread A grab a chunk of memory, split it to the needed amount, pass it by mut ref to thread B so that it can fill it up, then return a collection with the provided bump allocator
I would also like this blanket impl. Let me provide a different motivating example:
I have an Allocator subtrait trait MyAllocator: Allocator. It has methods that require a &mut self. I would like to create a blanket impl<A> MyAllocator for &mut A where A: MyAllocator but I can't do that if Allocator doesn't also have a blanket impl for &mut A. This blanket implementation is useful in generic code so I can take a mutable reference of any A: MyAllocator and also have it implement MyAllocator.
My use case in detail
I have written a BumpAllocator as a subtrait of Allocator that adds a method called prepare_allocation that returns the pointer range of the entire remaining space in its chunk of memory. The user can write into that space and then call allocate_prepared to mark it as used. This can be more performant because it allows for Vec-like type I call MutBumpVec that calls prepare_allocation at the start, giving it a large initial capacity, so it generally won't need to grow. It can later be turned into a slice, internally calling allocate_prepared, allowing for future allocations on that same bump allocator.
The methods actually take &self because I'm using Cell, but using them with a shared reference would be wildly unsafe. So I created a MutBumpAllocator marker trait that guarantees exclusive access to the bump allocator so I can write safe abstractions like the MutBumpVec. This MutBumpAllocator is a subtrait of BumpAllocator and both traits have a blanket impl for mutable references.
These types and traits are from my crate bump_scope. In this crate I've worked around this limitation by adding such a blanket impl for my own copy of the Allocator trait.
We discussed this in the @rust-lang/libs-api meeting and were convinced that this could be useful in practice. Please feel free to open a PR.