简体   繁体   中英

Cast &self to mut (to use predefined trait where it isn't mut)

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:

  • should they have made the &self mut or am I missing something?
  • how do I brutally cast this &self to being mut?

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<_, _>`

In short, 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 always 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 allocator you're actually using 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM