bigywater.tech
Rust · 75

panic!、unwrap 与 expect

panic 到底打印什么、退出码是多少、什么时候不会发生?unwrap 和 expect 的消息有什么区别?线程里 panic 会不会拖垮进程?这篇把九种常见 panic 的真实输出、overflow 的 debug/release 差异、线程与 hook 行为一起验证。

101
默认 panic 的进程退出码
debug / release
整数溢出的行为差异:panic 对回绕
SIGABRT
panic = "abort" 下进程被终止的信号

常见 panic 的消息与退出码

实测 · 本机执行输出
  编译:成功
  【Option::unwrap on None】退出码 101
      thread 'main' (7131770) panicked at <实验目录>/src/u75a.rs:1:42:
      called `Option::unwrap()` on a `None` value
  编译:成功
  【Result::unwrap on Err】退出码 101
      thread 'main' (7131822) panicked at <实验目录>/src/u75a.rs:1:66:
      called `Result::unwrap()` on an `Err` value: "磁盘满"
  编译:成功
  【expect 带自定义消息】退出码 101
      thread 'main' (7131869) panicked at <实验目录>/src/u75a.rs:1:42:
      配置里必须有端口
  编译:成功
  【Result::expect】退出码 101
      thread 'main' (7131919) panicked at <实验目录>/src/u75a.rs:1:81:
      解析端口: ParseIntError { kind: InvalidDigit }
  编译:成功
  【数组越界】退出码 101
      thread 'main' (7131968) panicked at <实验目录>/src/u75a.rs:1:90:
      index out of bounds: the len is 3 but the index is 5
  编译:成功
  【除以零】退出码 101
      thread 'main' (7132016) panicked at <实验目录>/src/u75a.rs:1:73:
      attempt to divide by zero
  编译:成功
  【RefCell 重复可变借用】退出码 101
      thread 'main' (7132065) panicked at <实验目录>/src/u75a.rs:1:85:
      RefCell already borrowed
  编译:成功
  【显式 panic! 带格式】退出码 101
      thread 'main' (7132112) panicked at <实验目录>/src/u75a.rs:1:24:
      状态非法: n = 3
  编译:成功
  【unreachable!】退出码 101
      thread 'main' (7132165) panicked at <实验目录>/src/u75a.rs:1:58:
      internal error: entered unreachable code: n = 1

整数溢出

实测 · 本机执行输出
  编译:成功
  debug  : 退出码 101  stdout=''  stderr=["thread 'main' (7132218) panicked at <实验目录>/src/u75b.rs:1:25:", 'attempt to add with overflow']
  编译:成功
  release: 退出码 0  stdout='x = 0'  stderr=[]

release 回绕是默认配置;可以通过 profile 的 overflow-checks 修改,这里没有验证。

不 panic 的替代

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
      expensive(unwrap_or) 被调用
    Some(1).unwrap_or(expensive) = 1
    Some(1).unwrap_or_else(expensive) = 1
      expensive(none) 被调用
    None.unwrap_or_else(expensive) = 42
    None.unwrap_or_default() = 0
      拿到错误: invalid digit found in string
    Err 的 unwrap_or_else 能看到错误: -1

线程中的 panic

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    join 得到 Err,载荷是 Some("子线程出错了");主线程继续运行
    主线程仍在运行
    
    thread '<unnamed>' (7132371) panicked at <实验目录>/src/u75d.rs:3:42:
    子线程出错了
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

展开与 catch_unwind 见catch_unwind。

hook 与 abort

实测 · 本机执行输出
  编译:成功
  运行(退出码 101):
    自定义 hook:出事了
      Drop 运行了
  panic = "abort":被信号 6(SIGABRT)终止(Python 报告返回码 -6);stdout='即将 panic'(没有 "Drop 运行了");stderr 最后一行: ['note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace']
  默认 unwind:    退出码 101;stdout='即将 panic\n  Drop 运行了'

Drop 的运行规则见Drop Trait 与资源释放。

分步演示

panic
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。panic = "abort" 通过 cargo profile.dev 设置
    • 没有验证:RUST_BACKTRACE 的回溯内容、panic 发生在 Drop 里(双重 panic 会 abort)、overflow-checks 的 profile 设置、no_std 下的 panic_handler、被 abort 终止时是否生成 core dump
    京ICP备2026067502号-1京公网安备11010502063487号