创建一个 `Pin<Box<[T; N]>>` 在 Rust 中,当 `[T; N]`太大,无法在堆栈上创建

Loc*_*cke 1 allocation rust maybeuninit

广义问题

如何pinned_array_of_default在稳定的 Rust 中实现一个[T; N]太大而无法放入堆栈的通用函数?

fn pinned_array_of_default<T: Default, const N: usize>() -> Pin<Box<[T; N]>> {
    unimplemented!()
}
Run Code Online (Sandbox Code Playgroud)

或者,如果这样可以使过程更容易,则T可以实施。Copy

fn pinned_array_of_element<T: Copy, const N: usize>(x: T) -> Pin<Box<[T; N]>> {
    unimplemented!()
}
Run Code Online (Sandbox Code Playgroud)

将解决方案保存在安全的 Rust 中会更好,但似乎不太可能。

方法

最初,我希望通过实现,Default我也许能够Default处理初始分配,但是它仍然在堆栈上创建它,因此这对于较大的 值不起作用N

let boxed: Box<[T; N]> = Box::default();
let foo = Pin::new(boxed);
Run Code Online (Sandbox Code Playgroud)

我怀疑我需要使用它MaybeUninit来实现这一点,并且有一个Box::new_uninit()函数,但它目前不稳定,我理想地希望将其保留在稳定的 Rust 中。我也有点不确定转变为Pin<Box<MaybeUninit<B>>>是否Pin<Box<B>>会对Pin.

背景

使用 a 的目的Pin<Box<[T; N]>>是保存一个指针块,其中N是页面大小的某个常数因子/倍数。

#[repr(C)]
#[derive(Copy, Clone)]
pub union Foo<R: ?Sized> {
    assigned: NonNull<R>,
    next_unused: Option<NonNull<Self>>,
}
Run Code Online (Sandbox Code Playgroud)

每个指针在给定时间点可能正在使用,也可能没有使用。in-useFoo指向R,unused/empty具有指向块中下Foo一个空的指针或。指向块中第一个未使用的指针被单独存储。当一个块已满时,将创建一个新块,然后未使用位置的指针链继续通过下一个块。FooNoneFoo

该框需要固定,因为它将包含自引用指针以及将指针保存到每个块中指定位置的外部结构。

我知道按照 Rust 标准,这是非常不安全的,但创建一个仍然存在的Foo普遍问题Pin<Box<[T; N]>>

kmd*_*eko 7

在堆上构造大型数组并避免在堆栈上创建它的一种方法是通过Vec. 您可以构造元素并使用.into_boxed_slice()来获取Box<[T]>. 然后您可以使用.try_into()将其转换为Box<[T; N]>. 然后使用.into()将其转换为Pin<Box<[T; N]>>

fn pinned_array_of_default<T: Default, const N: usize>() -> Pin<Box<[T; N]>> {
    let mut vec = vec![];
    vec.resize_with(N, T::default);
    let boxed: Box<[T; N]> = match vec.into_boxed_slice().try_into() {
        Ok(boxed) => boxed,
        Err(_) => unreachable!(),
    };
    
    boxed.into()
}
Run Code Online (Sandbox Code Playgroud)

如果您添加,您可以选择使其看起来更直接,T: Clone以便您可以执行vec![T::default(); N]和/或添加T: Debug,以便您可以使用.unwrap()or .expect()

也可以看看: