Reputation: 2182
I'm trying to implement Allocator:
pub unsafe trait Allocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
...
Note that the &self is not mutable. I'm not sure what kind of allocator would be able to allocate without making changes to the allocator itself, but this is what vec
is prepared to talk to so I guess I have to put up with it. My questions are:
I tried just sticking mut in the impl:
unsafe impl Allocator for Mappoc {
fn allocate(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
...
but it said:
|| error[E0053]: method `allocate` has an incompatible type for trait
src/lib.rs|110| 17
|| |
|| 110 | fn allocate(&mut self, layout: Layout)
|| | ^^^^^^^^^
|| | |
|| | types differ in mutability
|| | help: change the self-receiver type to match the trait: `self: &Mappoc`
|| |
|| = note: expected fn pointer `fn(&Mappoc, std::alloc::Layout) -> Result<_, _>`
|| found fn pointer `fn(&mut Mappoc, std::alloc::Layout) -> Result<_, _>`
Upvotes: 1
Views: 767
Reputation: 154996
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.
Upvotes: 6