Rust 不让你读没初始化的内存,可有时又需要避开清零的成本或逐个构造数组元素。MaybeUninit 就是这个出口。这篇验证它的大小、用法、drop 规则,并量出清零的代价。
源码:
1 | fn main() { let x: u32; println!("{}", x); }
编译:失败
error[E0381]: used binding `x` isn't initialized
--> <实验目录>/src/u118a.rs:1:40
|
1 | fn main() { let x: u32; println!("{}", x); }
| - ^ `x` used here but it isn't initialized
| |
| binding declared here but left uninitialized
|
help: consider assigning a value
|
1 | fn main() { let x: u32 = 42; println!("{}", x); }
| ++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0381`.
编译:成功
运行(退出码 0):
size_of::<u64>() = 8 size_of::<MaybeUninit<u64>>() = 8 align 同样 8 / 8
size_of::<Option<u64>>() = 16(Option 要标签)而 MaybeUninit<u64> 没有额外字节:它不记录是否已初始化
write 之后 assume_init = 42unsafe 的边界见unsafe 的安全边界。
编译:成功
运行(退出码 0):
逐个 write 之后整体转成 [String; 4]:["item-0", "item-1", "item-4", "item-9"]
对照:std::array::from_fn 的安全写法得到相同结果: true 编译:成功
运行(退出码 0):
1) MaybeUninit<Noisy> 离开作用域:
(没有看到 Noisy(1) drop —— MaybeUninit 不负责析构,值泄漏了)
2) 手动 assume_init_drop:
Noisy(2) drop
3) 数组只初始化了前 2 个,只 drop 这 2 个:
Noisy(10) drop
Noisy(11) dropDrop 的运行规则见Drop Trait 与资源释放。
编译:成功
运行(退出码 0):
vec 每次 11.07 µs
with_capacity + set_len(不清零) 每次 0.93 µs(只写了 1/4096 的字节,其余保持未初始化)vec![0u8; N] 可能走系统的清零分配(calloc),这里没有区分;数字是本机 release 微基准,且只写了极少数字节,仅用于说明量级。读取从未写入的内存是未定义行为,本文没有演示。