以下代码是否有声音?
#![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运行它没有问题。
更正:下面的答案仍然适用于一般情况,但在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]
的布局是相同的。
原始答案:
据我所知,这是可能有效但无法保证的事情之一,并且可能受特定于编译器或特定于平台的行为的影响。
MaybeUninit
在current 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)]
属性标记。
但在这种情况下,这不一定是真的。由于()
是零大小的类型,编译器可能会为T
和MaybeUninit<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>()