rust 对切片/“大小化”实现“Deref”

relj7zay  于 2023-01-17  发布在  其他
关注(0)|答案(2)|浏览(135)

我正在开发一个实现分配器的库(https://github.com/JonathanWoollett-Light/array-allocators),它可以在共享内存中使用。
我有一个当前在T: Sized上实现DerefDerefMut的类型,需要它在T: ?Sized上实现以支持切片。
此类型为TypedLinkedListArrayWrapper

pub struct TypedLinkedListArrayWrapper<'a, const N: usize, T: ?Sized> {
    pub wrapper: LinkedListArrayWrapper<'a, N>,
    __marker: PhantomData<T>,
}

它实现了DerefDerefMut

impl<'a, const N: usize, T> Deref for TypedLinkedListArrayWrapper<'a, N, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        unsafe { &*(self.wrapper.deref() as *const [LinkedListArrayBlock]).cast() }
    }
}
impl<'a, const N: usize, T> DerefMut for TypedLinkedListArrayWrapper<'a, N, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { &mut *(self.wrapper.deref_mut() as *mut [LinkedListArrayBlock]).cast() }
    }
}

如果将T更改为T:?Sized,则会出现以下错误

error[E0277]: the size for values of type `T` cannot be known at compilation time
   --> src/linked_list.rs:166:54
    |
162 | impl<'a, const N: usize, T: ?Sized> Deref for TypedLinkedListArrayWrapper<'a, N, T> {
    |                          - this type parameter needs to be `std::marker::Sized`
...
166 |         unsafe { &*std::ptr::addr_of!(*self.wrapper).cast() }
    |                                                      ^^^^ doesn't have a size known at compile-time
    |
note: required by a bound in `ptr::const_ptr::<impl *const T>::cast`
   --> /home/jonathan/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/const_ptr.rs:60:23
    |
60  |     pub const fn cast<U>(self) -> *const U {
    |                       ^ required by this bound in `ptr::const_ptr::<impl *const T>::cast`
help: consider removing the `?Sized` bound to make the type parameter `Sized`
    |
162 - impl<'a, const N: usize, T: ?Sized> Deref for TypedLinkedListArrayWrapper<'a, N, T> {
162 + impl<'a, const N: usize, T> Deref for TypedLinkedListArrayWrapper<'a, N, T> {
    |

For more information about this error, try `rustc --explain E0277`.
error: could not compile `array-allocators` due to previous error

这是在LinkedListArrayWrapper之上:

pub struct LinkedListArrayWrapper<'a, const N: usize> {
    allocator: &'a LinkedListArrayAllocator<N>,
    index: usize,
    size: usize,
}

它实现了DerefDerefMut

impl<'a, const N: usize> Deref for LinkedListArrayWrapper<'a, N> {
    type Target = [LinkedListArrayBlock];

    fn deref(&self) -> &Self::Target {
        let allocator = unsafe { &*self.allocator.0.get() };
        &allocator.data[self.index..self.index + self.size]
    }
}
impl<'a, const N: usize> DerefMut for LinkedListArrayWrapper<'a, N> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        let allocator = unsafe { &mut *self.allocator.0.get() };
        &mut allocator.data[self.index..self.index + self.size]
    }
}

其中LinkedListArrayBlock为:

pub struct LinkedListArrayBlock {
    size: usize,
    next: Option<usize>,
}

我该怎么做呢?

qhhrdooz

qhhrdooz1#

所以我找到了解决办法。
这里的一个重要主题与https://doc.rust-lang.org/std/ptr/trait.Pointee.html有关。不幸的是,这是每晚的,需要#![feature(ptr_metadata)]
一种解决方案是使用切片的自定义类型来嵌入切片所需的 meta数据:

pub struct SliceLinkedListArrayWrapper<'a, const N: usize, T> {
    pub wrapper: LinkedListArrayWrapper<'a, N>,
    pub len: usize,
    __marker: PhantomData<T>,
}
impl<'a, const N: usize, T> Deref for SliceLinkedListArrayWrapper<'a, N, T> {
    type Target = [T];

    fn deref(&self) -> &Self::Target {
        unsafe { &*std::ptr::from_raw_parts(std::ptr::addr_of!(*self.wrapper).cast(), self.len) }
    }
}
impl<'a, const N: usize, T> DerefMut for SliceLinkedListArrayWrapper<'a, N, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe {
            &mut *std::ptr::from_raw_parts_mut(
                std::ptr::addr_of_mut!(*self.wrapper).cast(),
                self.len,
            )
        }
    }
}

更一般化的解决方案可能是可能的,尽管可能是笨拙的。

ha5z0ras

ha5z0ras2#

这里的具体问题是.cast()要求目标类型为Sized,原因可以在issue介绍方法中找到:
NonNull::cast一样,输入指针类型不需要是Sized,但输出指针类型需要,因为我们不知道要插入什么样的指针元数据(切片长度、trait对象vtable等)。
撇开这些不谈,我完全不明白你想干什么。看起来你想用LinkedListArrayBlock的一个切片作为T的后备存储?这看起来非常可疑,而且非常不合理。即使不是这样,你也不能在胖指针之间随意地进行转换,让它有意义。

相关问题