如何使用 #![feature(const_mut_refs)]?

解读

在国内 Rust 岗位面试中,const_mut_refs 属于“Nightly-only 特性”,面试官通常想确认三件事:

  1. 候选人是否清楚“稳定版(stable)不能用”这一红线;
  2. 是否理解 const 上下文里为何默认禁止 &mut
  3. 能否给出 最小可编译示例 并解释其 UB 边界
    回答时务必先强调“仅用于实验或内部工具链,上线代码必须走 RFC 稳定流程”,否则会被直接判为“工程素养不足”。

知识点

  1. const fn 的求值发生在编译期,内存模型是“编译时虚拟栈”,mutable borrow 一旦逃逸到运行期会造成双重借用,属于 UB
  2. #![feature(const_mut_refs)] 打开后,const fn 内部可对指向编译时常量的 &mut T 进行写操作,但写结果不会回写到最终二进制,仅影响编译期求值。
  3. 使用限制
    • 必须 nightly toolchainrustup default nightly
    • 必须 crate 级别打开:#![feature(const_mut_refs)]
    • 不允许&mut 返回给调用者,否则编译器会报 “mutable reference in const fn”
  4. const_evaluatable_checkedconst_fn_trait_bound 等特性交叉时,需关注 feature 依赖顺序;CI 中必须 pin nightly 版本,防止语法突变。
  5. 国内银行、券商、车联网等安全审计场景,源码扫描工具(如 Rust-SEC、自研 SIG)会直接拦截任何 #![feature(...)];面试中应补充“如要上线,需等待 trait 稳定或改用 macro/typenum 等零成本替代方案”。

答案

  1. 环境准备

    rustup install nightly
    rustup default nightly
    rustup component add rust-src
    
  2. 新建实验 crate

    cargo new --lib const_mut_demo
    cd const_mut_demo
    
  3. src/lib.rs 顶部整 crate 声明特性

    #![feature(const_mut_refs)]
    
    pub const fn add_one(x: &mut u32) {
        *x += 1;
    }
    
    // 编译期求值,运行期不可见
    pub const VAL: u32 = {
        let mut a = 10;
        add_one(&mut a);
        a
    };
    
    #[cfg(test)]
    mod tests {
        use super::*;
        #[test]
        fn it_works() {
            // 运行期拿到的是编译期计算后的常量
            assert_eq!(VAL, 11);
        }
    }
    
  4. 编译验证

    cargo +nightly test --release
    

    若误用 stable,会收到 error[E0554]: #![feature] may not be used on the stable release channel
    若试图 返回 &mut 给调用者,会收到 error[E0658]: mutable references in const fn are unstable

  5. 上线替代方案

    • const-generic + trait 实现编译期递归,避免 &mut
    • macro_rules! 生成常量数组;
    • 等待 const_mut_refs 进入 beta/stable 并经过 RFC 评审后再迁移。

拓展思考

  1. 如果面试官追问“为何 Rust 不让 const fn 返回 &mut T”,可从 MIRI 的 Stacked Borrows 模型角度回答:编译期内存地址在二进制加载时不再存在,返回的引用会指向无效地址,造成悬垂指针
  2. 对比 C++20 consteval:C++ 允许 constexpr mutable 但禁止 mutable 逃逸到运行期;Rust 的设计更严格,直接语法层面封杀,体现“编译通过即正确”的保守哲学。
  3. 国内大型 Rust 项目(如某云原生网络代理)的实践经验:把 const 计算拆成独立 crate,用 build.rs + nightly 在编译机生成常量表,最终产物用 stable 链接,既利用 nightly 特性,又满足生产审计要求。面试中若能提到这一“双工具链隔离”套路,可大幅加分。