Rust中生成(非解引用)悬垂引用为何引发UB?能否被观测?
《Rust参考手册》与《Rustonomicon》均明确规定:生成悬垂引用属于未定义行为(Undefined Behaviour,UB)。以下是触发该UB的示例代码:
fn main() { let p: std::ptr::NonNull<u8> = std::ptr::NonNull::dangling(); #[allow(unused_variables)] let r: &u8 = unsafe { std::mem::transmute::<_, _>(p) }; }
通过Rust Playground中的Miri运行这段代码,会直接触发UB报错:
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (address 0x1 is unallocated) --> src/main.rs:4:27 | 4 | let r: &u8 = unsafe { std::mem::transmute::<_, _>(p) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x1 is unallocated) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: = note: inside `main` at src/main.rs:4:27
针对你提出的两个问题,解答如下:
1. Rust中生成(非解引用)悬垂引用为何会导致UB?
Rust的引用存在核心不变式(invariants):所有引用必须始终指向有效的、对齐的、非空的内存;共享引用不能存在活跃的可变借用,可变引用不能存在其他任何借用。这些不变式是Rust编译器进行优化、保证内存安全的基础。
编译器会无条件信任这些不变式,比如它会假设引用指向的内存永远存活,因此可以大胆进行代码重排、常量传播、删除冗余检查等优化。如果允许生成悬垂引用,就直接破坏了这些不变式——编译器基于“引用有效”的假设生成的机器码,在实际运行时会因为引用悬垂而出现不可预测的行为。
此外,Rust的安全类型系统完全依赖这些不变式构建,生成悬垂引用相当于绕过了安全检查的边界,破坏了语言的内存安全模型,因此被定义为UB。
2. 能否在不解引用该引用的情况下观测到该UB?
可以。最直接的例子就是使用Miri这样的运行时验证工具:Miri会在程序执行过程中检查引用的有效性,一旦检测到悬垂引用被创建,就会立刻抛出错误,这个过程不需要解引用引用。
在实际的硬件执行环境中,虽然不会立刻触发崩溃,但编译器的优化可能会导致程序行为异常。比如编译器可能会基于“引用有效”的假设,将某些变量直接存入寄存器,或者删除它认为不必要的代码逻辑,最终导致程序输出不符合预期——而这一切都不需要你解引用那个悬垂引用。
内容的提问来源于stack exchange,提问作者Jonas Schäfer

