Cast &self to mut (to use predefined trait where it isn't mut)
As pointed out in the comments, casting &self
to &mut
is undefined behavior and is not allowed even in unsafe code (though the compiler is powerless to prevent you from doing it in an unsafe block). Fortunately, it's not needed.
allocate()
takes &self
to allow the allocator to be used from multiple threads. (Remember that a &mut
reference is exclusive, only one may exist at a time.) The simplest thread-safe way to get a mutable reference out of an immutable one is by wrapping the actual allocator in a mutex:
struct MappocAllocator {
inner: Mutex<Mappoc>, // your actual allocator
}
impl Allocator for MappocAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let alloc = self.inner.lock().unwrap();
// now you have access to `&mut Mappoc` for the duration of the lock
...
}
}
I'm not sure what kind of allocator would be able to allocate without making changes to the allocator itself
This is a misunderstanding of what &T
means. A shared reference doesn't necessarily imply that the data under it won't change, it means that it's safe to use by multiple actors at once. For example, lock-free mutating APIs always take &self
.
If the Mappoc
allocator is written in Rust and is thread-safe (or partly/fully lock-free) itself, then its methods should take &self
to begin with, and you won't need a mutex (because a mutex or its equivalent will be part of Mappoc
implementation). If Mappoc
's methods take &mut self
, it means they're not safe to be invoked from multiple threads, and it's a good thing that Rust forces you to access them through a mutex. This is the system working exactly as designed.
Finally, some allocators, like mimalloc
or jemalloc
, are implemented in C or C++ that does its own locking. But then their Rust fronts don't need &mut self
either because they invoke the actual allocator through a raw pointer.