Union std::mem::MaybeUninit
1.36.0 · source · #[repr(transparent)]
pub union MaybeUninit<T> {
/* private fields */
}
Expand description
包装器类型,用于创建 T
的未初始化实例。
初始化不变量
通常,编译器假定已根据变量类型的要求正确初始化了变量。例如,引用类型的变量必须对齐且非空。 即使在不安全的代码中,这也必须始终保持不变。 因此,将引用类型的变量初始化为零会导致瞬时 未定义的行为,无论该引用是否曾经用于访问内存:
use std::mem::{self, MaybeUninit};
let x: &i32 = unsafe { mem::zeroed() }; // 未定义的行为! ⚠️
// 与 `MaybeUninit<&i32>` 等效的代码:
let x: &i32 = unsafe { MaybeUninit::zeroed().assume_init() }; // 未定义的行为! ⚠️
Run编译器利用这一点进行各种优化,例如取消运行时检查和优化 enum
布局。
同样,完全未初始化的存储器可以包含任何内容,而 bool
必须始终为 true
或 false
。因此,创建未初始化的 bool
是未定义的行为:
use std::mem::{self, MaybeUninit};
let b: bool = unsafe { mem::uninitialized() }; // 未定义的行为! ⚠️
// 与 `MaybeUninit<bool>` 等效的代码:
let b: bool = unsafe { MaybeUninit::uninit().assume_init() }; // 未定义的行为! ⚠️
Run此外,未初始化的存储器的特殊之处在于它没有固定的值 (“fixed” 表示 “it won’t change without being written to”)。多次读取相同的未初始化字节会产生不同的结果。 这使得在变量中具有未初始化的数据成为未定义的行为,即使该变量具有整数类型也可以保留任何固定位模式
use std::mem::{self, MaybeUninit};
let x: i32 = unsafe { mem::uninitialized() }; // 未定义的行为! ⚠️
// 与 `MaybeUninit<i32>` 等效的代码:
let x: i32 = unsafe { MaybeUninit::uninit().assume_init() }; // 未定义的行为! ⚠️
Run最重要的是,请记住,大多数类型都有额外的不变量,而不仅仅是在类型级别被初始化。
例如,将 1
初始化的 Vec<T>
视为已初始化 (在当前实现下; 这并不构成稳定的保证),因为编译器知道的唯一要求是数据指针必须为非空值。
创建这样的 Vec<T>
不会立即导致未定义的行为,但是在大多数安全操作 (包括丢弃操作) 中都将导致未定义的行为。
Examples
MaybeUninit<T>
用于启用不安全代码来处理未初始化的数据。
这是向编译器发出的信号,指示此处的数据可能不被初始化:
use std::mem::MaybeUninit;
// 创建一个显式未初始化的引用。
// 编译器知道 `MaybeUninit<T>` 内部的数据可能无效,因此不是 UB:
let mut x = MaybeUninit::<&i32>::uninit();
// 将其设置为有效值。
x.write(&0);
// 提取已初始化的数据 - 仅在正确初始化 `x` 之后 * 才允许这样做!
let x = unsafe { x.assume_init() };
Run然后,编译器知道不会对此代码进行任何错误的假设或优化。
您可以认为 MaybeUninit<T>
有点像 Option<T>
,但是没有任何运行时跟踪且没有任何安全检查。
out-pointers
您可以使用 MaybeUninit<T>
来实现 “out-pointers”: 与其从函数中返回数据,还不如将其传递给某个 (uninitialized) 内存的指针以将结果放入其中。
当对调用者来说,控制结果存储在内存中的分配方式很重要并且您希望避免不必要的移动时,这很有用。
use std::mem::MaybeUninit;
unsafe fn make_vec(out: *mut Vec<i32>) {
// `write` 不丢弃旧内容,这一点很重要。
out.write(vec![1, 2, 3]);
}
let mut v = MaybeUninit::uninit();
unsafe { make_vec(v.as_mut_ptr()); }
// 现在我们知道 `v` 已初始化! 这也可以确保正确丢弃 vector。
let v = unsafe { v.assume_init() };
assert_eq!(&v, &[1, 2, 3]);
Run逐元素初始化数组
MaybeUninit<T>
可用于逐个元素初始化大数组:
use std::mem::{self, MaybeUninit};
let data = {
// 创建一个未初始化的 `MaybeUninit` 数组。
// `assume_init` 是安全的,因为我们声称这里已经初始化的类型是一堆 `MaybeUninit`,不需要初始化。
let mut data: [MaybeUninit<Vec<u32>>; 1000] = unsafe {
MaybeUninit::uninit().assume_init()
};
// 丢弃 `MaybeUninit` 什么都不做
for elem in &mut data[..] {
elem.write(vec![42]);
}
// 一切都已初始化。
// 将数组转换为初始化的类型。
unsafe { mem::transmute::<_, [Vec<u32>; 1000]>(data) }
};
assert_eq!(&data[0], &[42]);
Run您也可以使用部分初始化的数组,这些数组可以在不稳定的数据结构中找到。
use std::mem::MaybeUninit;
// 创建一个未初始化的 `MaybeUninit` 数组。
// `assume_init` 是安全的,因为我们声称这里已经初始化的类型是一堆 `MaybeUninit`,不需要初始化。
let mut data: [MaybeUninit<String>; 1000] = unsafe { MaybeUninit::uninit().assume_init() };
// 计算我们分配的元素数。
let mut data_len: usize = 0;
for elem in &mut data[0..500] {
elem.write(String::from("hello"));
data_len += 1;
}
// 对于数组中的每个项,如果我们分配了它,则将其丢弃。
for elem in &mut data[0..data_len] {
unsafe { elem.assume_init_drop(); }
}
Run逐字段初始化结构体
您可以使用 MaybeUninit<T>
和 std::ptr::addr_of_mut
宏来逐字段初始化结构体:
use std::mem::MaybeUninit;
use std::ptr::addr_of_mut;
#[derive(Debug, PartialEq)]
pub struct Foo {
name: String,
list: Vec<u8>,
}
let foo = {
let mut uninit: MaybeUninit<Foo> = MaybeUninit::uninit();
let ptr = uninit.as_mut_ptr();
// 初始化 `name` 字段 使用 `write` 而不是通过 `=` 赋值,而不是在旧的、未初始化的值上调用 `drop`。
unsafe { addr_of_mut!((*ptr).name).write("Bob".to_string()); }
// 初始化 `list` 字段如果此处存在 panic,则 `name` 字段中的 `String` 泄漏。
unsafe { addr_of_mut!((*ptr).list).write(vec![0, 1, 2]); }
// 所有字段都已初始化,因此我们调用 `assume_init` 来获取已初始化的 Foo。
unsafe { uninit.assume_init() }
};
assert_eq!(
foo,
Foo {
name: "Bob".to_string(),
list: vec![0, 1, 2]
}
);
RunLayout
MaybeUninit<T>
保证与 T
具有相同的大小、对齐方式和 ABI:
use std::mem::{MaybeUninit, size_of, align_of};
assert_eq!(size_of::<MaybeUninit<u64>>(), size_of::<u64>());
assert_eq!(align_of::<MaybeUninit<u64>>(), align_of::<u64>());
Run但是请记住,包含MaybeUninit<T>
的类型不一定是相同的布局。Rust 通常不保证 Foo<T>
的字段具有与 Foo<U>
相同的顺序,即使 T
和 U
具有相同的大小和对齐方式。
此外,由于任何位值对于 MaybeUninit<T>
都是有效的,因此编译器无法应用 non-zero/niche-filling 优化,从而可能导致更大的大小:
assert_eq!(size_of::<Option<bool>>(), 1);
assert_eq!(size_of::<Option<MaybeUninit<bool>>>(), 2);
Run如果 T
是 FFI 安全的,则 MaybeUninit<T>
也是如此。
虽然 MaybeUninit
是 #[repr(transparent)]
(表示它保证与 T
相同的大小,对齐方式和 ABI),但是这 不会 更改任何先前的警告。
Option<T>
和 Option<MaybeUninit<T>>
可能仍然具有不同的大小,并且包含 T
类型字段的类型的布局 (和大小) 可能与该字段为 MaybeUninit<T>
时不同。
MaybeUninit
是 union 类型,union 上的 #[repr(transparent)]
不稳定 (参见 跟踪问题).
随着时间的推移,对 union 的 #[repr(transparent)]
的确切保证可能会发生变化,并且 MaybeUninit
可能会或可能不会保留 #[repr(transparent)]
。
就是说,MaybeUninit<T>
将总是保证它具有与 T
有相同的大小、对齐方式和 ABI; 只是 MaybeUninit
实现保证的方式可能会演变。
Implementations§
source§impl<T, A> Box<MaybeUninit<T>, A>where
A: Allocator,
impl<T, A> Box<MaybeUninit<T>, A>where A: Allocator,
sourcepub unsafe fn assume_init(self) -> Box<T, A>
🔬This is a nightly-only experimental API. (new_uninit
#63291)
pub unsafe fn assume_init(self) -> Box<T, A>
new_uninit
#63291)转换为 Box<T, A>
。
Safety
与 MaybeUninit::assume_init
一样,由调用者负责确保该值确实处于初始化状态。
在内容尚未完全初始化时调用此方法会立即导致未定义的行为。
Examples
#![feature(new_uninit)]
let mut five = Box::<u32>::new_uninit();
let five: Box<u32> = unsafe {
// 延迟初始化:
five.as_mut_ptr().write(5);
five.assume_init()
};
assert_eq!(*five, 5)
Runsourcepub fn write(boxed: Box<MaybeUninit<T>, A>, value: T) -> Box<T, A>
🔬This is a nightly-only experimental API. (new_uninit
#63291)
pub fn write(boxed: Box<MaybeUninit<T>, A>, value: T) -> Box<T, A>
new_uninit
#63291)写入值并转换为 Box<T, A>
。
这种方法将 box 转换成和 Box::assume_init
类似的形式,只是在转换前将 value
写入其中,从而保证了安全性。
在某些情况下,使用此方法可能会提高性能,因为编译器可能能够优化从栈复制。
Examples
#![feature(new_uninit)]
let big_box = Box::<[usize; 1024]>::new_uninit();
let mut array = [0; 1024];
for (i, place) in array.iter_mut().enumerate() {
*place = i;
}
// 优化器可能会忽略此副本,所以以前的代码直接写入到堆中。
let big_box = Box::write(big_box, array);
for (i, x) in big_box.iter().enumerate() {
assert_eq!(*x, i);
}
Runsource§impl<T> MaybeUninit<T>
impl<T> MaybeUninit<T>
const: 1.36.0 · sourcepub const fn new(val: T) -> MaybeUninit<T>
pub const fn new(val: T) -> MaybeUninit<T>
创建一个使用给定值初始化的新 MaybeUninit<T>
。
在此函数的返回值上调用 assume_init
是安全的。
注意,丢弃 MaybeUninit<T>
永远不会调用 T 的丢弃代码。
确保 T
在初始化时被丢弃是您的责任。
Example
use std::mem::MaybeUninit;
let v: MaybeUninit<Vec<u8>> = MaybeUninit::new(vec![42]);
Runconst: 1.36.0 · sourcepub const fn uninit() -> MaybeUninit<T>
pub const fn uninit() -> MaybeUninit<T>
const: unstable · sourcepub fn uninit_array<const N: usize>() -> [MaybeUninit<T>; N]
🔬This is a nightly-only experimental API. (maybe_uninit_uninit_array
#96097)
pub fn uninit_array<const N: usize>() -> [MaybeUninit<T>; N]
maybe_uninit_uninit_array
#96097)在未初始化状态下创建 MaybeUninit<T>
项的新数组。
Note: 在 future Rust 版本中,当 Rust 允许 内联常量表达式 时,此方法可能变得不必要。
下面的示例可以使用 let mut buf = [const { MaybeUninit::<u8>::uninit() }; 32];
。
Examples
#![feature(maybe_uninit_uninit_array, maybe_uninit_slice)]
use std::mem::MaybeUninit;
extern "C" {
fn read_into_buffer(ptr: *mut u8, max_len: usize) -> usize;
}
/// 返回实际读取的 (可能较小的) 数据切片
fn read(buf: &mut [MaybeUninit<u8>]) -> &[u8] {
unsafe {
let len = read_into_buffer(buf.as_mut_ptr() as *mut u8, buf.len());
MaybeUninit::slice_assume_init_ref(&buf[..len])
}
}
let mut buf: [MaybeUninit<u8>; 32] = MaybeUninit::uninit_array();
let data = read(&mut buf);
Runconst: unstable · sourcepub fn zeroed() -> MaybeUninit<T>
pub fn zeroed() -> MaybeUninit<T>
在未初始化状态下创建新的 MaybeUninit<T>
,并用 0
字节填充内存。取决于 T
是否已经进行了正确的初始化。
例如,初始化 MaybeUninit<usize>::zeroed()
,但不初始化 MaybeUninit<&'static i32>::zeroed()
,因为引用不能为空。
注意,丢弃 MaybeUninit<T>
永远不会调用 T 的丢弃代码。
确保 T
在初始化时被丢弃是您的责任。
Example
此函数的正确用法:用零初始化结构体,其中结构体的所有字段都可以将位模式 0 保留为有效值。
use std::mem::MaybeUninit;
let x = MaybeUninit::<(u8, bool)>::zeroed();
let x = unsafe { x.assume_init() };
assert_eq!(x, (0, false));
Run该函数的 错误 用法:当 0
不是该类型的有效位模式时,调用 x.zeroed().assume_init()
:
use std::mem::MaybeUninit;
enum NotZero { One = 1, Two = 2 }
let x = MaybeUninit::<(u8, NotZero)>::zeroed();
let x = unsafe { x.assume_init() };
// 在一个对中,我们创建一个没有有效判别式的 `NotZero`。
// 这是未定义的行为。⚠️
Run1.55.0 (const: unstable) · sourcepub fn write(&mut self, val: T) -> &mut T
pub fn write(&mut self, val: T) -> &mut T
设置 MaybeUninit<T>
的值。
这将覆盖任何先前的值而不将其丢弃,因此请注意不要重复使用此两次,除非您要跳过运行析构函数。
为了您的方便,这也将 self
的内容 (现在已安全初始化) 返回变量引用。
由于内容存储在 MaybeUninit
中,如果 MaybeUninit 离开作用域而没有调用到 assume_init
、assume_init_drop
或类似对象,则不会为内部数据运行析构函数。
接收该函数返回的可变引用的代码需要记住这一点。
Rust 的安全模型认为泄漏是安全的,但它们通常仍然是不可取的。
话虽这么说,但借用引用与其他任何一个借用引用一样,因此为其赋予新的值将摒弃旧的内容。
Examples
正确使用此方法:
use std::mem::MaybeUninit;
let mut x = MaybeUninit::<Vec<u8>>::uninit();
{
let hello = x.write((&b"Hello, world!").to_vec());
// 设置 hello 不会预先分配,但不会泄漏它们
*hello = (&b"Hello").to_vec();
hello[0] = 'h' as u8;
}
// x 现在初始化:
let s = unsafe { x.assume_init() };
assert_eq!(b"hello", s.as_slice());
Run该方法的这种用法会导致泄漏:
use std::mem::MaybeUninit;
let mut x = MaybeUninit::<String>::uninit();
x.write("Hello".to_string());
// 这会泄漏包含的字符串:
x.write("hello".to_string());
// x 现在初始化:
let s = unsafe { x.assume_init() };
Run这种方法可以用来避免在某些情况下不安全。下面的例子展示了一个固定大小的 arena 实现的一部分,它提供了固定的引用。
有了 write
,我们就可以避免通过裸指针路径来写:
use core::pin::Pin;
use core::mem::MaybeUninit;
struct PinArena<T> {
memory: Box<[MaybeUninit<T>]>,
len: usize,
}
impl <T> PinArena<T> {
pub fn capacity(&self) -> usize {
self.memory.len()
}
pub fn push(&mut self, val: T) -> Pin<&mut T> {
if self.len >= self.capacity() {
panic!("Attempted to push to a full pin arena!");
}
let ref_ = self.memory[self.len].write(val);
self.len += 1;
unsafe { Pin::new_unchecked(ref_) }
}
}
Runconst: 1.59.0 · sourcepub const fn as_ptr(&self) -> *const T
pub const fn as_ptr(&self) -> *const T
获取指向所包含值的指针。
除非初始化 MaybeUninit<T>
,否则从该指针读取或将其转换为 quot 是未定义的行为。
写入该指针 (non-transitively) 指向的内存是未定义的行为 (UnsafeCell<T>
内部除外)。
Examples
正确使用此方法:
use std::mem::MaybeUninit;
let mut x = MaybeUninit::<Vec<u32>>::uninit();
x.write(vec![0, 1, 2]);
// 在 `MaybeUninit<T>` 中创建引用。可以,因为我们已将其初始化。
let x_vec = unsafe { &*x.as_ptr() };
assert_eq!(x_vec.len(), 3);
Run这个方法的错误用法:
use std::mem::MaybeUninit;
let x = MaybeUninit::<Vec<u32>>::uninit();
let x_vec = unsafe { &*x.as_ptr() };
// 我们创建了对未初始化的 vector 的引用! 这是未定义的行为。⚠️
Run(请注意,围绕未初始化数据引用的规则尚未最终确定,但是除非被确定,否则建议避免使用它们。)
const: unstable · sourcepub fn as_mut_ptr(&mut self) -> *mut T
pub fn as_mut_ptr(&mut self) -> *mut T
获取指向包含值的可变指针。
除非初始化 MaybeUninit<T>
,否则从该指针读取或将其转换为 quot 是未定义的行为。
Examples
正确使用此方法:
use std::mem::MaybeUninit;
let mut x = MaybeUninit::<Vec<u32>>::uninit();
x.write(vec![0, 1, 2]);
// 在 `MaybeUninit<Vec<u32>>` 中创建引用。
// 可以,因为我们已将其初始化。
let x_vec = unsafe { &mut *x.as_mut_ptr() };
x_vec.push(3);
assert_eq!(x_vec.len(), 4);
Run这个方法的错误用法:
use std::mem::MaybeUninit;
let mut x = MaybeUninit::<Vec<u32>>::uninit();
let x_vec = unsafe { &mut *x.as_mut_ptr() };
// 我们创建了对未初始化的 vector 的引用! 这是未定义的行为。⚠️
Run(请注意,围绕未初始化数据引用的规则尚未最终确定,但是除非被确定,否则建议避免使用它们。)
const: 1.59.0 · sourcepub const unsafe fn assume_init(self) -> T
pub const unsafe fn assume_init(self) -> T
从 MaybeUninit<T>
容器中提取值。这是确保数据将被丢弃的好方法,因为生成的 T
受到通常的丢弃处理。
Safety
取决于调用者,以确保 MaybeUninit<T>
确实处于初始化状态。在内容尚未完全初始化时调用此方法会立即导致未定义的行为。
类型级文档 中包含了有关此初始化不变量的更多信息。
最重要的是,请记住,大多数类型都有额外的不变量,而不仅仅是在类型级别被初始化。
例如,将 1
初始化的 Vec<T>
视为已初始化 (在当前实现下; 这并不构成稳定的保证),因为编译器知道的唯一要求是数据指针必须为非空值。
创建这样的 Vec<T>
不会立即导致未定义的行为,但是在大多数安全操作 (包括丢弃操作) 中都将导致未定义的行为。
Examples
正确使用此方法:
use std::mem::MaybeUninit;
let mut x = MaybeUninit::<bool>::uninit();
x.write(true);
let x_init = unsafe { x.assume_init() };
assert_eq!(x_init, true);
Run这个方法的错误用法:
use std::mem::MaybeUninit;
let x = MaybeUninit::<Vec<u32>>::uninit();
let x_init = unsafe { x.assume_init() };
// `x` 尚未初始化,因此最后一行导致未定义的行为。⚠️
Run1.60.0 (const: unstable) · sourcepub unsafe fn assume_init_read(&self) -> T
pub unsafe fn assume_init_read(&self) -> T
从 MaybeUninit<T>
容器中读取值。结果 T
受通常的 drop 处理影响。
只要有可能,最好改用 assume_init
,这样可以防止重复 MaybeUninit<T>
的内容。
Safety
取决于调用者,以确保 MaybeUninit<T>
确实处于初始化状态。在内容尚未完全初始化时调用此方法会导致未定义的行为。
类型级文档 中包含了有关此初始化不变量的更多信息。
此外,类似于 ptr::read
函数,该函数创建内容的按位副本,无论所包含的类型是否实现 Copy
trait。
当使用数据的多个副本时 (通过多次调用 assume_init_read
,或先调用 assume_init_read
再调用 assume_init
),您有责任确保数据确实可能重复。
Examples
正确使用此方法:
use std::mem::MaybeUninit;
let mut x = MaybeUninit::<u32>::uninit();
x.write(13);
let x1 = unsafe { x.assume_init_read() };
// `u32` 是 `Copy`,因此我们可能会读取多次。
let x2 = unsafe { x.assume_init_read() };
assert_eq!(x1, x2);
let mut x = MaybeUninit::<Option<Vec<u32>>>::uninit();
x.write(None);
let x1 = unsafe { x.assume_init_read() };
// 复制 `None` 值是可以的,因此我们可能会多次读取。
let x2 = unsafe { x.assume_init_read() };
assert_eq!(x1, x2);
Run这个方法的错误用法:
use std::mem::MaybeUninit;
let mut x = MaybeUninit::<Option<Vec<u32>>>::uninit();
x.write(Some(vec![0, 1, 2]));
let x1 = unsafe { x.assume_init_read() };
let x2 = unsafe { x.assume_init_read() };
// 现在,我们创建了同一 vector 的两个副本,当它们都被丢弃时,将导致双重释放!
Run1.60.0 · sourcepub unsafe fn assume_init_drop(&mut self)
pub unsafe fn assume_init_drop(&mut self)
将包含的值放置到位。
如果您拥有 MaybeUninit
,您也可以使用 assume_init
作为替代。
Safety
取决于调用者,以确保 MaybeUninit<T>
确实处于初始化状态。
在内容尚未完全初始化时调用此方法会导致未定义的行为。
最重要的是,必须满足类型 T
的所有附加不变量,因为 T
(或其变体) 的 Drop
实现可能依赖于此。
例如,将 Vec<T>
设置为无效但非空的地址使其初始化 (在当前实现下;
这并不构成稳定的保证),因为编译器知道的唯一要求是数据指针必须非空。
但是,丢弃这样的 Vec<T>
会导致不确定的行为。
1.55.0 (const: 1.59.0) · sourcepub const unsafe fn assume_init_ref(&self) -> &T
pub const unsafe fn assume_init_ref(&self) -> &T
获取对包含的值的共享引用。
当我们要访问已初始化但没有 MaybeUninit
所有权 (防止使用 .assume_init()
) 的 MaybeUninit
时,这很有用。
Safety
在内容尚未完全初始化时调用此方法会导致未定义的行为:取决于调用者,以确保 MaybeUninit<T>
确实处于初始化状态。
Examples
正确使用此方法:
use std::mem::MaybeUninit;
let mut x = MaybeUninit::<Vec<u32>>::uninit();
// 初始化 `x`:
x.write(vec![1, 2, 3]);
// 现在已知我们的 `MaybeUninit<_>` 已初始化,可以创建对其的共享引用:
let x: &Vec<u32> = unsafe {
// SAFETY: `x` 已初始化。
x.assume_init_ref()
};
assert_eq!(x, &vec![1, 2, 3]);
Run这个方法的错误用法:
use std::mem::MaybeUninit;
let x = MaybeUninit::<Vec<u32>>::uninit();
let x_vec: &Vec<u32> = unsafe { x.assume_init_ref() };
// 我们创建了对未初始化的 vector 的引用! 这是未定义的行为。⚠️
Runuse std::{cell::Cell, mem::MaybeUninit};
let b = MaybeUninit::<Cell<bool>>::uninit();
// 使用 `Cell::set` 初始化 `MaybeUninit`:
unsafe {
b.assume_init_ref().set(true);
// ^^^^^^^^^^^^^^^
// 引用未初始化的 `Cell<bool>`: UB!
}
Run1.55.0 (const: unstable) · sourcepub unsafe fn assume_init_mut(&mut self) -> &mut T
pub unsafe fn assume_init_mut(&mut self) -> &mut T
获取所包含值的可变 (unique) 引用。
当我们要访问已初始化但没有 MaybeUninit
所有权 (防止使用 .assume_init()
) 的 MaybeUninit
时,这很有用。
Safety
在内容尚未完全初始化时调用此方法会导致未定义的行为:取决于调用者,以确保 MaybeUninit<T>
确实处于初始化状态。
例如,.assume_init_mut()
不能用于初始化 MaybeUninit
。
Examples
正确使用此方法:
use std::mem::MaybeUninit;
extern "C" {
/// 初始化所有输入缓冲区的字节。
fn initialize_buffer(buf: *mut [u8; 1024]);
}
let mut buf = MaybeUninit::<[u8; 1024]>::uninit();
// 初始化 `buf`:
unsafe { initialize_buffer(buf.as_mut_ptr()); }
// 现在我们知道 `buf` 已被初始化,因此我们可以对其进行 `.assume_init()`。
// 但是,使用 `.assume_init()` 可能会触发 1024 字节的 `memcpy`。
// 为了断言我们的缓冲区已经初始化而不复制它,我们将 `&mut MaybeUninit<[u8; 1024]>` 升级为 `&mut [u8; 1024]`:
let buf: &mut [u8; 1024] = unsafe {
// SAFETY: `buf` 已初始化。
buf.assume_init_mut()
};
// 现在我们可以将 `buf` 用作普通切片:
buf.sort_unstable();
assert!(
buf.windows(2).all(|pair| pair[0] <= pair[1]),
"buffer is sorted",
);
Run这个方法的错误用法:
您不能使用 .assume_init_mut()
初始化值:
use std::mem::MaybeUninit;
let mut b = MaybeUninit::<bool>::uninit();
unsafe {
*b.assume_init_mut() = true;
// 我们已经创建了 (mutable) 引用未初始化的 `bool`!
// 这是未定义的行为。⚠️
}
Run例如,您不能 Read
进入未初始化的缓冲区:
use std::{io, mem::MaybeUninit};
fn read_chunk (reader: &'_ mut dyn io::Read) -> io::Result<[u8; 64]>
{
let mut buffer = MaybeUninit::<[u8; 64]>::uninit();
reader.read_exact(unsafe { buffer.assume_init_mut() })?;
// ^^^^^^^^^^^^^^^^^^^^^^^^
// (mutable) 引用到未初始化的内存!
// 这是未定义的行为。
Ok(unsafe { buffer.assume_init() })
}
Run也不能使用直接字段访问来进行逐字段逐步初始化:
use std::{mem::MaybeUninit, ptr};
struct Foo {
a: u32,
b: u8,
}
let foo: Foo = unsafe {
let mut foo = MaybeUninit::<Foo>::uninit();
ptr::write(&mut foo.assume_init_mut().a as *mut u32, 1337);
// ^^^^^^^^^^^^^^^^^^^^^
// (mutable) 引用到未初始化的内存!
// 这是未定义的行为。
ptr::write(&mut foo.assume_init_mut().b as *mut u8, 42);
// ^^^^^^^^^^^^^^^^^^^^^
// (mutable) 引用到未初始化的内存!
// 这是未定义的行为。
foo.assume_init()
};
Runconst: unstable · sourcepub unsafe fn array_assume_init<const N: usize>(
array: [MaybeUninit<T>; N]
) -> [T; N]
🔬This is a nightly-only experimental API. (maybe_uninit_array_assume_init
#96097)
pub unsafe fn array_assume_init<const N: usize>( array: [MaybeUninit<T>; N] ) -> [T; N]
maybe_uninit_array_assume_init
#96097)从 MaybeUninit
容器数组中提取值。
Safety
调用者有责任保证数组的所有元素都处于初始化状态。
Examples
#![feature(maybe_uninit_uninit_array)]
#![feature(maybe_uninit_array_assume_init)]
use std::mem::MaybeUninit;
let mut array: [MaybeUninit<i32>; 3] = MaybeUninit::uninit_array();
array[0].write(0);
array[1].write(1);
array[2].write(2);
// SAFETY: 现在安全了,因为我们初始化了所有元素
let array = unsafe {
MaybeUninit::array_assume_init(array)
};
assert_eq!(array, [0, 1, 2]);
Runconst: unstable · sourcepub unsafe fn slice_assume_init_ref(slice: &[MaybeUninit<T>]) -> &[T]
🔬This is a nightly-only experimental API. (maybe_uninit_slice
#63569)
pub unsafe fn slice_assume_init_ref(slice: &[MaybeUninit<T>]) -> &[T]
maybe_uninit_slice
#63569)假设所有元素都已初始化,请对其进行切片。
Safety
取决于调用者,以确保 MaybeUninit<T>
元素确实处于初始化状态。
在内容尚未完全初始化时调用此方法会导致未定义的行为。
有关更多详细信息和示例,请参见 assume_init_ref
。
const: unstable · sourcepub unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit<T>]) -> &mut [T]
🔬This is a nightly-only experimental API. (maybe_uninit_slice
#63569)
pub unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit<T>]) -> &mut [T]
maybe_uninit_slice
#63569)假设所有元素都已初始化,请为其获取可变切片。
Safety
取决于调用者,以确保 MaybeUninit<T>
元素确实处于初始化状态。
在内容尚未完全初始化时调用此方法会导致未定义的行为。
有关更多详细信息和示例,请参见 assume_init_mut
。
const: unstable · sourcepub fn slice_as_ptr(this: &[MaybeUninit<T>]) -> *const T
🔬This is a nightly-only experimental API. (maybe_uninit_slice
#63569)
pub fn slice_as_ptr(this: &[MaybeUninit<T>]) -> *const T
maybe_uninit_slice
#63569)获取指向数组第一个元素的指针。
const: unstable · sourcepub fn slice_as_mut_ptr(this: &mut [MaybeUninit<T>]) -> *mut T
🔬This is a nightly-only experimental API. (maybe_uninit_slice
#63569)
pub fn slice_as_mut_ptr(this: &mut [MaybeUninit<T>]) -> *mut T
maybe_uninit_slice
#63569)获取指向数组第一个元素的可变指针。
sourcepub fn write_slice<'a>(this: &'a mut [MaybeUninit<T>], src: &[T]) -> &'a mut [T]where
T: Copy,
🔬This is a nightly-only experimental API. (maybe_uninit_write_slice
#79995)
pub fn write_slice<'a>(this: &'a mut [MaybeUninit<T>], src: &[T]) -> &'a mut [T]where T: Copy,
maybe_uninit_write_slice
#79995)从 src
复制元素,将 this
现在初始化的内容返回给 this
的资源引用。
如果 T
未实现 Copy
,请使用 write_slice_cloned
这类似于 slice::copy_from_slice
。
Panics
如果两个切片的长度不同,则此函数将为 panic。
Examples
#![feature(maybe_uninit_write_slice)]
use std::mem::MaybeUninit;
let mut dst = [MaybeUninit::uninit(); 32];
let src = [0; 32];
let init = MaybeUninit::write_slice(&mut dst, &src);
assert_eq!(init, src);
Run#![feature(maybe_uninit_write_slice)]
use std::mem::MaybeUninit;
let mut vec = Vec::with_capacity(32);
let src = [0; 16];
MaybeUninit::write_slice(&mut vec.spare_capacity_mut()[..src.len()], &src);
// SAFETY: 我们刚刚将 len 的所有元素复制到了备用容量中,vec 的第一个 src.len() 元素现在有效。
unsafe {
vec.set_len(src.len());
}
assert_eq!(vec, src);
Runsourcepub fn write_slice_cloned<'a>(
this: &'a mut [MaybeUninit<T>],
src: &[T]
) -> &'a mut [T]where
T: Clone,
🔬This is a nightly-only experimental API. (maybe_uninit_write_slice
#79995)
pub fn write_slice_cloned<'a>( this: &'a mut [MaybeUninit<T>], src: &[T] ) -> &'a mut [T]where T: Clone,
maybe_uninit_write_slice
#79995)将元素从 src
克隆到 this
,返回对 this
现在初始化内容的可变引用。
任何已经初始化的元素都不会被丢弃。
如果 T
实现 Copy
,请使用 write_slice
这类似于 slice::clone_from_slice
,但不会丢弃现有元素。
Panics
如果两个切片的长度不同,或者 Clone
panics 的实现,则此函数将为 panic。
如果存在 panic,将丢弃已经克隆的元素。
Examples
#![feature(maybe_uninit_write_slice)]
use std::mem::MaybeUninit;
let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()];
let src = ["wibbly".to_string(), "wobbly".to_string(), "timey".to_string(), "wimey".to_string(), "stuff".to_string()];
let init = MaybeUninit::write_slice_cloned(&mut dst, &src);
assert_eq!(init, src);
Run#![feature(maybe_uninit_write_slice)]
use std::mem::MaybeUninit;
let mut vec = Vec::with_capacity(32);
let src = ["rust", "is", "a", "pretty", "cool", "language"];
MaybeUninit::write_slice_cloned(&mut vec.spare_capacity_mut()[..src.len()], &src);
// SAFETY: 我们刚刚将 len 的所有元素克隆到了备用容量中,vec 的第一个 src.len() 元素现在有效。
unsafe {
vec.set_len(src.len());
}
assert_eq!(vec, src);
Runsourcepub fn as_bytes(&self) -> &[MaybeUninit<u8>]
🔬This is a nightly-only experimental API. (maybe_uninit_as_bytes
#93092)
pub fn as_bytes(&self) -> &[MaybeUninit<u8>]
maybe_uninit_as_bytes
#93092)将此 MaybeUninit
的内容作为可能未初始化的字节切片返回。
请注意,即使 MaybeUninit
的内容已初始化,该值仍可能包含未初始化的填充字节。
Examples
#![feature(maybe_uninit_as_bytes, maybe_uninit_slice)]
use std::mem::MaybeUninit;
let val = 0x12345678_i32;
let uninit = MaybeUninit::new(val);
let uninit_bytes = uninit.as_bytes();
let bytes = unsafe { MaybeUninit::slice_assume_init_ref(uninit_bytes) };
assert_eq!(bytes, val.to_ne_bytes());
Runsourcepub fn as_bytes_mut(&mut self) -> &mut [MaybeUninit<u8>]
🔬This is a nightly-only experimental API. (maybe_uninit_as_bytes
#93092)
pub fn as_bytes_mut(&mut self) -> &mut [MaybeUninit<u8>]
maybe_uninit_as_bytes
#93092)返回此 MaybeUninit
的内容作为可能未初始化字节的可变切片返回。
请注意,即使 MaybeUninit
的内容已初始化,该值仍可能包含未初始化的填充字节。
Examples
#![feature(maybe_uninit_as_bytes)]
use std::mem::MaybeUninit;
let val = 0x12345678_i32;
let mut uninit = MaybeUninit::new(val);
let uninit_bytes = uninit.as_bytes_mut();
if cfg!(target_endian = "little") {
uninit_bytes[0].write(0xcd);
} else {
uninit_bytes[3].write(0xcd);
}
let val2 = unsafe { uninit.assume_init() };
assert_eq!(val2, 0x123456cd);
Runsourcepub fn slice_as_bytes(this: &[MaybeUninit<T>]) -> &[MaybeUninit<u8>]
🔬This is a nightly-only experimental API. (maybe_uninit_as_bytes
#93092)
pub fn slice_as_bytes(this: &[MaybeUninit<T>]) -> &[MaybeUninit<u8>]
maybe_uninit_as_bytes
#93092)将此 MaybeUninit
切片的内容作为可能未初始化的字节切片返回。
请注意,即使 MaybeUninit
的内容已初始化,该值仍可能包含未初始化的填充字节。
Examples
#![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)]
use std::mem::MaybeUninit;
let uninit = [MaybeUninit::new(0x1234u16), MaybeUninit::new(0x5678u16)];
let uninit_bytes = MaybeUninit::slice_as_bytes(&uninit);
let bytes = unsafe { MaybeUninit::slice_assume_init_ref(&uninit_bytes) };
let val1 = u16::from_ne_bytes(bytes[0..2].try_into().unwrap());
let val2 = u16::from_ne_bytes(bytes[2..4].try_into().unwrap());
assert_eq!(&[val1, val2], &[0x1234u16, 0x5678u16]);
Runsourcepub fn slice_as_bytes_mut(this: &mut [MaybeUninit<T>]) -> &mut [MaybeUninit<u8>]
🔬This is a nightly-only experimental API. (maybe_uninit_as_bytes
#93092)
pub fn slice_as_bytes_mut(this: &mut [MaybeUninit<T>]) -> &mut [MaybeUninit<u8>]
maybe_uninit_as_bytes
#93092)将 MaybeUninit
的可变切片的内容作为可能未初始化字节的可变切片返回。
请注意,即使 MaybeUninit
的内容已初始化,该值仍可能包含未初始化的填充字节。
Examples
#![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)]
use std::mem::MaybeUninit;
let mut uninit = [MaybeUninit::<u16>::uninit(), MaybeUninit::<u16>::uninit()];
let uninit_bytes = MaybeUninit::slice_as_bytes_mut(&mut uninit);
MaybeUninit::write_slice(uninit_bytes, &[0x12, 0x34, 0x56, 0x78]);
let vals = unsafe { MaybeUninit::slice_assume_init_ref(&uninit) };
if cfg!(target_endian = "little") {
assert_eq!(vals, &[0x3412u16, 0x7856u16]);
} else {
assert_eq!(vals, &[0x1234u16, 0x5678u16]);
}
Runsource§impl<T, const N: usize> MaybeUninit<[T; N]>
impl<T, const N: usize> MaybeUninit<[T; N]>
sourcepub const fn transpose(self) -> [MaybeUninit<T>; N]
🔬This is a nightly-only experimental API. (maybe_uninit_uninit_array_transpose
#96097)
pub const fn transpose(self) -> [MaybeUninit<T>; N]
maybe_uninit_uninit_array_transpose
#96097)Trait Implementations§
source§impl<T> Clone for MaybeUninit<T>where
T: Copy,
impl<T> Clone for MaybeUninit<T>where T: Copy,
source§fn clone(&self) -> MaybeUninit<T>
fn clone(&self) -> MaybeUninit<T>
1.0.0 · source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
执行复制分配。 Read more