转换MaybeUninit <[T; N]>到[MaybeUninit <t>; N]?

时间:2019-03-23 11:54:54

标签: rust

以下代码是否正确?

#![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可以毫无问题地运行它。

1 个答案:

答案 0 :(得分:5)

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

首先,MaybeUninit的文档中有一个layout部分,说明了这一点

  

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

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

  

对数组进行布局,以使数组的nth元素与数组的开头偏移n * the size of the type个字节。 [T; n]数组的大小为size_of::<T>() * n,对齐方式为T

这意味着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>]有一个{{1} },用于数组中的每个b

MaybeUninit<T>