将 &self 强制转换为 mut (在非 mut 的情况下使用预定义特征)

Adr*_*May 1 rust

我正在尝试实现分配器:

pub unsafe trait Allocator {
    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
    ...
Run Code Online (Sandbox Code Playgroud)

请注意,&self 是不可变的。我不确定什么样的分配器能够在不更改分配器本身的情况下进行分配,但这是vec准备与之交谈的,所以我想我必须忍受它。我的问题是:

  • 他们应该制作 &self mut 还是我错过了什么?
  • 我该如何残酷地让这个自我变得沉默寡言?

我尝试在 impl 中添加 mut:

unsafe impl Allocator for Mappoc {                         
    fn allocate(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { 
    ...
Run Code Online (Sandbox Code Playgroud)

但它说:

|| 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<_, _>`
Run Code Online (Sandbox Code Playgroud)

use*_*342 6

正如评论中指出的,强制转换&self&mut未定义的行为,即使在不安全的代码中也是不允许的(尽管编译器无法阻止您在不安全的块中执行此操作)。幸运的是,不需要它。

allocate()需要&self允许从多个线程使用分配器。(请记住,&mut引用是独占的,一次只能存在一个。)从不可变引用中获取可变引用的最简单的线程安全方法是将实际分配器包装在互斥体中:

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
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

我不确定什么样的分配器能够在不更改分配器本身的情况下进行分配

这是对含义的误解&T。共享引用并不一定意味着其下的数据不会更改,这意味着多个参与者可以安全地同时使用它。例如,无锁变异 API 始终采用&self.

如果Mappoc分配器是用 Rust 编写的并且本身是线程安全的(或部分/完全无锁),那么它的方法应该&self首先使用,并且您不需要互斥体(因为互斥体或其等效物将是的Mappoc实施)。如果Mappoc的方法 take &mut self,则意味着从多个线程调用它们是不安全的,而且 Rust 强制您通过互斥体访问它们是一件好事。这是完全按照设计工作的系统。

最后,一些分配器(例如mimallocor jemalloc)是用 C 或 C++ 实现的,它们具有自己的锁定功能。但他们的 Rust 前端也不需要,&mut self因为他们通过原始指针调用实际的分配器。