将MaybeUninit转换为[MaybeUninit ; N]?

问题描述 投票:7回答:1

以下代码是否有声音?

#![feature(maybe_uninit)]
use std::mem;
const N: usize = 2; // or another number
type T = String; // or any other type

fn main() {
    unsafe {
        // create an uninitialized array
        let t: mem::MaybeUninit<[T; N]> = mem::MaybeUninit::uninitialized();
        // convert it to an array of uninitialized values
        let mut t: [mem::MaybeUninit<T>; N] = mem::transmute(t);
        // initialize the values
        t[0].set("Hi".to_string());
        t[1].set("there".to_string());
        // use the values
        println!("{} {}", t[0].get_ref(), t[1].get_ref());
        // drop the values
        mem::replace(&mut t[0], mem::MaybeUninit::uninitialized()).into_initialized();
        mem::replace(&mut t[1], mem::MaybeUninit::uninitialized()).into_initialized();
    }
}

我应该注意到miri运行它没有问题。

rust
1个回答
5
投票

更正:下面的答案仍然适用于一般情况,但在MaybeUninit的情况下,有一些关于内存布局的方便特殊情况,这使得这实际上是安全的:

首先,MaybeUninit的文档有一个layout部分说明

MaybeUninit<T>保证与T具有相同的大小和对齐方式。

其次,语言参考说明了array layouts

布局数组使得数组的nth元素通过n * the size of the type字节从数组的开头偏移。 [T; n]阵列的大小为size_of::<T>() * nT的排列方式相同。

这意味着MaybeUninit<[T; n]>的布局和[MaybeUninit<T>; n]的布局是相同的。


原始答案:

据我所知,这是可能有效但无法保证的事情之一,并且可能受特定于编译器或特定于平台的行为的影响。

MaybeUninitcurrent source中的定义如下:

#[allow(missing_debug_implementations)]
#[unstable(feature = "maybe_uninit", issue = "53491")]
pub union MaybeUninit<T> {
    uninit: (),
    value: ManuallyDrop<T>,
}

因为它没有用#[repr]属性标记(与ManuallyDrop相反),所以它是默认表示,其中引用says this

没有repr属性的标称类型具有默认表示。非正式地,这种表示也称为锈表示。

无法保证此表示形式的数据布局。

为了从Wrapper<[T]>转换到[Wrapper<T>],必须是Wrapper<T>的内存布局与T的内存布局完全相同的情况。对于许多包装器来说就是这种情况,例如前面提到的ManuallyDrop,那些通常会用#[repr(transparent)]属性标记。

但在这种情况下,这不一定是真的。由于()是零大小的类型,编译器可能会为TMaybeUninit<T>使用相同的内存布局(这就是为什么它适合你),但编译器也可能决定使用其他内存布局(例如,为了优化目的),在这种情况下,转换将不再起作用。


作为一个具体示例,编译器可以选择为MaybeUninit<T>使用以下内存布局:

+---+---+...+---+
| T         | b |     where b is "is initialized" flag
+---+---+...+---+

根据以上引用,允许编译器执行此操作。在这种情况下,[MaybeUninit<T>]MaybeUninit<[T]>有不同的内存布局,因为MaybeUninit<[T]>对整个数组有一个b,而[MaybeUninit<T>]对于数组中的每个b有一个MaybeUninit<T>

MaybeUninit<[T]>:
+---+...+---+---+...+---+...+---+...+---+---+
| T[0]      | T[1]      | … | T[n-1]    | b |
+---+...+---+---+...+---+...+---+...+---+---+
Total size: n * size_of::<T>() + 1

[MaybeUninit<T>]
+---+...+---+----+---+...+---+----+...+---+...+---+------+
| T[0]      |b[0]| T[1]      |b[1]| … | T[n-1]    |b[n-1]|
+---+...+---+----+---+...+---+----+...+---+...+---+------+
Total size: (n + 1) * size_of::<T>()
© www.soinside.com 2019 - 2024. All rights reserved.