bigywater.tech
Rust · 84

编译期断言

把不变量检查从运行期挪到编译期:结构体大小、泛型参数范围……这篇用 const assert、关联常量和 inline const 块验证它们什么时候求值、报什么错、有没有运行期成本。

E0080
常量求值失败的错误码
0 行
通过的 const 断言给汇编增加的行数(逐行相同)
instantiating
泛型断言失败时 note 里指出具体实例的关键词

const 断言

实测 · 本机执行输出
  源码:
      1 | #[repr(C)] struct Header { tag: u8, len: u32 }
      2 | const _: () = assert!(std::mem::size_of::<Header>() == 8, "Header 必须是 8 字节");
      3 | fn main() { println!("size = {}", std::mem::size_of::<Header>()); }
  编译:成功
  运行(退出码 0):
    size = 8
  源码:
      1 | #[repr(C)] struct Header { tag: u8, len: u32 }
      2 | const _: () = assert!(std::mem::size_of::<Header>() == 6, "Header 必须是 6 字节");
      3 | fn main() { println!("size = {}", std::mem::size_of::<Header>()); }
  编译:失败
    error[E0080]: evaluation panicked: Header 必须是 6 字节
     --> <实验目录>/src/u84b.rs:2:15
      |
    2 | const _: () = assert!(std::mem::size_of::<Header>() == 6, "Header 必须是 6 字节");
      |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0080`.

零运行期成本

实测 · 本机执行输出
  有 const 断言的 f 汇编 2 行,没有断言的 2 行,逐行相同: True

泛型里的断言

实测 · 本机执行输出
  源码:
      1 | struct Buf<const N: usize>;
      2 | impl<const N: usize> Buf<N> {
      3 |     const OK: () = assert!(N.is_power_of_two(), "N 必须是 2 的幂");
      4 |     fn new() -> Self { let () = Self::OK; Buf }
      5 | }
      6 | fn main() { let _a = Buf::<8>::new(); println!("Buf::<8> 创建成功"); }
  编译:成功
  运行(退出码 0):
    Buf::<8> 创建成功
  源码:
      1 | struct Buf<const N: usize>;
      2 | impl<const N: usize> Buf<N> {
      3 |     const OK: () = assert!(N.is_power_of_two(), "N 必须是 2 的幂");
      4 |     fn new() -> Self { let () = Self::OK; Buf }
      5 | }
      6 | fn main() { let _a = Buf::<8>::new(); let _b = Buf::<6>::new(); }
  编译:失败
    error[E0080]: evaluation panicked: N 必须是 2 的幂
     --> <实验目录>/src/u84e.rs:3:20
      |
    3 |     const OK: () = assert!(N.is_power_of_two(), "N 必须是 2 的幂");
      |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `Buf::<6>::OK` failed here
    
    note: erroneous constant encountered
     --> <实验目录>/src/u84e.rs:4:33
      |
    4 |     fn new() -> Self { let () = Self::OK; Buf }
      |                                 ^^^^^^^^
    
    note: the above error was encountered while instantiating `fn Buf::<6>::new`
     --> <实验目录>/src/u84e.rs:6:48
      |
    6 | fn main() { let _a = Buf::<8>::new(); let _b = Buf::<6>::new(); }
      |                                                ^^^^^^^^^^^^^^^
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0080`.

inline const 块

实测 · 本机执行输出
  源码:
      1 | fn first_n<const N: usize>() -> [u8; N] { const { assert!(N > 0 && N <= 4, "N 必须在 1..=4") }; [0; N] }
      2 | fn main() { println!("{:?}", first_n::<3>()); }
  编译:成功
  运行(退出码 0):
    [0, 0, 0]
  源码:
      1 | fn first_n<const N: usize>() -> [u8; N] { const { assert!(N > 0 && N <= 4, "N 必须在 1..=4") }; [0; N] }
      2 | fn main() { println!("{:?}", first_n::<9>()); }
  编译:失败
    error[E0080]: evaluation panicked: N 必须在 1..=4
     --> <实验目录>/src/u84g.rs:1:51
      |
    1 | fn first_n<const N: usize>() -> [u8; N] { const { assert!(N > 0 && N <= 4, "N 必须在 1..=4") }; [0; N] }
      |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `first_n::<9>::{constant#1}` failed here
    
    note: erroneous constant encountered
     --> <实验目录>/src/u84g.rs:1:43
      |
    1 | fn first_n<const N: usize>() -> [u8; N] { const { assert!(N > 0 && N <= 4, "N 必须在 1..=4") }; [0; N] }
      |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    note: the above error was encountered while instantiating `fn first_n::<9>`
     --> <实验目录>/src/u84g.rs:2:30
      |
    2 | fn main() { println!("{:?}", first_n::<9>()); }
      |                              ^^^^^^^^^^^^^^
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0080`.

数组长度技巧

实测 · 本机执行输出
  源码:
      1 | const _: [(); 0 - (std::mem::size_of::<u32>() != 4) as usize] = [];
      2 | const _: [(); 0 - (std::mem::size_of::<u32>() != 5) as usize] = [];
      3 | fn main() {}
  编译:失败
    error[E0080]: attempt to compute `0_usize - 1_usize`, which would overflow
     --> <实验目录>/src/u84h.rs:2:15
      |
    2 | const _: [(); 0 - (std::mem::size_of::<u32>() != 5) as usize] = [];
      |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_::{constant#0}` failed here
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0080`.

布局相关的例子见Struct 对齐与 Padding。

分步演示

编译期断言
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。汇编用 rustc --emit=asm -O
    • 没有验证:static_assertions crate 的宏(没有下载使用)、const fn 里更复杂的断言(循环、字符串比较)、assert_eq! 在 const 里的使用(当前稳定版可能受限,未测)、debug_assert! 的区别
    京ICP备2026067502号-1京公网安备11010502063487号