You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Rust单元测试不会因悬垂指针触发panic?

问题分析:为何main触发panic而单元测试正常运行

首先看这段存在未定义行为(UB)的代码:

fn dangle() {
    fn get_str<'a>(s: *const String) -> &'a str {
        unsafe { &*s }
    }
    
    let s = String::from("hello");
    let dangling = get_str(&s);
    drop(s);
    println!("Invalid str: {}", dangling);
}

#[test]
fn test() {
    dangle();  // 预期panic但未触发
}

fn main() {
    dangle();  // 触发panic
}

核心原因:未定义行为的不确定性 + 测试环境的内存特性

这段代码的本质是创建了悬垂引用:drop(s)释放了String的底层内存后,dangling这个&str还指向这块已被释放的内存,后续println访问它属于标准的未定义行为。

未定义行为的特点就是没有固定预期表现——它可能崩溃、可能输出乱码、可能看似正常运行,完全取决于当前的运行环境、内存分配器状态、编译器优化等因素。而main和测试函数的运行环境存在关键差异:

  • 内存分配器行为差异:Rust在测试模式下使用的内存分配器(默认配置)通常不会立即回收或覆盖刚释放的内存块。当测试中执行println时,原来的"hello"数据还留在内存里,所以访问时没有触发错误。而main函数运行在普通环境中,内存可能被系统立即回收或被其他操作覆盖,访问已释放内存就触发了段错误(表现为panic)。
  • 运行环境的优化差异:测试模式下编译器的优化级别通常较低(默认是opt-level=0),而main函数如果是release编译会有更高优化,这也会影响内存布局和回收策略,进一步导致行为差异。

重要提示

这不是Rust的漏洞,完全是未定义行为的必然结果。任何依赖这种"偶然正常"的代码都是严重错误,生产环境中可能在任何时候突然崩溃或产生诡异的bug。正确的做法是绝对避免悬垂引用,遵循Rust的安全规则,不要用unsafe构造这种违反生命周期的引用。

内容的提问来源于stack exchange,提问作者realwangliqiu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 14:04:56