将ZST实例存入空指针的Rust代码行为是否符合定义?
解引用指向ZST的空指针是否属于未定义行为?
根据《Rustonomicon》中的描述:
Rust通常认为任何生成或存储零大小类型(ZST)的操作都可简化为无操作(no-op)
但同时又有明确规则:
注意,指向ZST的引用(包括空切片)与其他所有引用一样,必须非空且对齐正确。解引用空指针或未对齐指针指向ZST属于未定义行为(UB),与其他类型一致。
这两段规则看似矛盾,比如下面这段代码在rustc 1.75.0下可以正常编译运行,但它的行为是否符合Rust语言定义?
use std::ptr; fn main() { let null_ptr = ptr::null_mut::<()>(); unsafe { *null_ptr = () } }
结论:这段代码属于未定义行为
虽然存储ZST的操作会被编译器优化为无操作,但Rust的语言规范优先于编译器优化细节。解引用指针的前提条件是指针必须合法——即非空且满足对齐要求,这个规则对所有类型都适用,包括ZST。
编译器会基于“解引用的指针都是合法的”这一假设进行优化,哪怕实际操作最终被简化为no-op,违反前提的代码依然属于UB。当前版本编译器可能不会触发错误,但未来版本可能因为这个假设生成导致程序崩溃或异常的代码。
替代方案
如果你的需求是避免分配器为ZST分配非零大小的内存,可以使用NonNull::dangling()来获取一个合法的、非空且对齐的指针,专门用于ZST场景:
use std::ptr::NonNull; fn main() { let dangling_ptr = NonNull::<()>::dangling(); unsafe { *dangling_ptr.as_mut() = () } }
该指针不会指向任何实际内存,但满足Rust对指针合法性的要求,同时存储ZST的操作依然会被优化为无操作,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Some Name
相关产品推荐
相关产品推荐

