Description
#10 got a little too confusing, so I think it's good to separate this sub-issue into its own issue.
Currently, the Alloc
trait looks like this:
pub unsafe trait Alloc {
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr>;
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout);
// ...
}
I propose that we add an associated type called Err
. All methods that currently return Result<_, AllocErr>
would return Result<_, Self::Err>
.
pub unsafe trait Alloc {
type Err: Debug;
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, Self::Err>;
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout);
// ...
}
Then, the std::alloc
module would contain an AbortAlloc
(or something named similarly).
#[cold]
fn alloc_abort<E: Debug>(err: E, layout: Layout) -> ! {
eprintln!("Allocator error: {:?} with layout: {:?}", err, layout);
std::process::abort()
}
pub struct AbortAlloc<A: Alloc>(A);
unsafe impl<A: Alloc> Alloc for AbortAlloc<A> {
type Err = !;
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, !> {
self.0.alloc(layout).map_err(|e| alloc_abort(e, layout))
}
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
self.0.dealloc(ptr, layout)
}
// The rest of the methods would simply fall through in the same way.
// ...
}
impl<A: Alloc> From<A> for AbortAlloc<A> {
fn from(allocator: A) -> Self {
AbortAlloc(allocator)
}
}
Then, types like Vec
could have their normal methods that interact with the allocator only be implemented when the Alloc::Err
type is !
. Otherwise, the try_*
methods would be available which would propagate the error upwards.
Additionally, the new_in
/etc methods would take an Into<A>
parameter so that the user experience of creating an infallible collection wouldn't require the user to explicitly wrap their allocator in an AbortAlloc
.