如何 const panic?

解读

在 Rust 面试里,面试官问“如何 const panic?”并不是想听你背标准库 API,而是考察三点:

  1. 你是否理解 const 上下文运行时上下文 的本质区别;
  2. 你是否掌握 编译期求值(const eval) 的边界与演进;
  3. 你是否能在 稳定版(stable) 与 ** nightly** 之间给出可落地的工程方案,并权衡稳定性与可读性。

国内一线大厂(华为、阿里、字节、PingCAP 等)在 2024 年校招/社招中,已把“const 能力”作为系统岗的硬性区分度:能在 stable 上写出不依赖 panic_any! 的编译期错误,是“资深”与“入门”的分水岭。

知识点

  1. const fn 的约束:

    • 不能分配堆内存、不能使用裸指针解引用、不能调用非 const fn。
    • 在 Rust 1.57 之前,panic! 宏本身不是 const,因此编译期无法直接 panic。
  2. const_panic 特性(nightly)

    • Rust 1.57 起,nightly 引入 #![feature(const_panic)],允许在 const fn 里直接调用 panic!()
    • 该特性已于 2021-11-进入 “milestone-1”,预计 2025 年稳定,但目前仍需 nightly
  3. stable 上的“编译期陷阱”技巧

    • 利用 数组长度必须是 usize 且编译期已知 的规则,制造 let _ = [(); 0][1]; 这类越界访问,触发 编译器硬错误(compile_error!)
    • 该技巧零成本、零运行时开销,且兼容 1.56+ stable,是国内生产环境事实标准
  4. const_eval_select(nightly 实验)

    • 允许在 const 上下文与运行时上下文分别给出两份实现,可做到“编译期 panic,运行期正常返回”,但 API 仍在迭代,不推荐生产使用
  5. panic vs compile_error!

    • compile_error! 只能出现在宏展开位置,无法出现在 const fn 内部;因此不能替代 const panic。
    • 真正的 const panic 必须让编译期求值引擎自己终止,并给出用户自定义消息。

答案

stable Rust(1.70+) 上,最简洁且工程可落地的写法是:

const fn div_ceil(a: u32, b: u32) -> u32 {
    // 检查除零:编译期即报错,无需 nightly
    let _ = [()][(b == 0) as usize];
    (a + b - 1) / b
}

解释:

  • (b == 0) as usize 得到 0 或 1;当为 1 时访问 [()][1] 越界,编译器在求值 const 上下文时立即 panic,并输出 “index out of bounds” 消息。
  • 该写法无需任何 feature flag,在 CI、crates.io 发布均合规,字节跳动与华为开源项目已大规模使用。

若团队允许 nightly,可直接使用官方语法糖:

#![feature(const_panic)]

const fn div_ceil(a: u32, b: u32) -> u32 {
    if b == 0 {
        panic!("divisor must be non-zero");
    }
    (a + b - 1) / b
}

面试时,先给出 stable 方案,再补充 nightly 演进,可体现你对国内“稳定性优先”工程文化的理解。

拓展思考

  1. const panic 消息如何自定义?
    stable 上目前无法携带自定义字符串;越界陷阱只能触发默认 panic 信息。若业务强制需要“可读错误”,可封装 proc-macro,在宏里用 compile_error! 拼接字符串,但代价是把运行时参数变成宏输入,失去 const fn 的灵活性。

  2. const panic 与 #![no_std] 兼容吗?
    完全兼容。越界陷阱不依赖 panic_handler,也不依赖 alloc,裸机、内核、嵌入式均可用。在 rCore、AliOS 内核分支中,该技巧已用于静态配置校验。

  3. 未来稳定后,是否应全网替换“越界陷阱”?
    不建议一次性重构。越界陷阱零依赖、零版本门槛,而 const_panic 稳定后仍需 MSRV 1.80+。国内金融、信创场景普遍锁版本到 1.70 以下,两套方案并存将是常态。面试时可强调“渐进迁移 + CI 双测”策略,展示你对国内交付节奏的务实认知。