这是一个定义条件编译宏的代码片段,用于 glam 库的运行时和编译时断言。
rust
#[cfg(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
))]
macro_rules! glam_assert {
($($arg:tt)*) => ( assert!($($arg)*); )
}
#[cfg(not(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
)))]
macro_rules! glam_assert {
($($arg:tt)*) => {};
}
macro_rules! const_assert {
($x:expr $(,)?) => {
#[allow(unknown_lints, clippy::eq_op)]
const _: () = assert!($x);
};
}
macro_rules! const_assert_eq {
($x:expr, $y:expr $(,)?) => {
const_assert!($x == $y);
};
}
1. glam_assert! 宏 - 运行时断言
条件编译逻辑
rust
#[cfg(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
))]
这个条件表示:当满足任一条件时启用断言:
debug_assertions启用 且debug-glam-assert特性启用(调试模式下的断言)glam-assert特性启用(所有模式下的断言)
两种实现
启用断言时的实现:
rust
macro_rules! glam_assert {
($($arg:tt)*) => ( assert!($($arg)*); )
}
- 接收任意参数(
$($arg:tt)*) - 直接转发给标准的
assert!宏 - 在条件满足时执行实际的断言检查
禁用断言时的实现:
rust
#[cfg(not(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
)))]
macro_rules! glam_assert {
($($arg:tt)*) => {};
}
- 当条件不满足时,宏展开为空
- 相当于在编译时移除所有断言,零运行时开销
使用示例
rust
// 在代码中使用
fn some_function(normalized_vector: Vec3) {
glam_assert!(normalized_vector.is_normalized()); // 仅在启用时检查
// 函数逻辑...
}
2. const_assert! 宏 - 编译时常量断言
rust
macro_rules! const_assert {
($x:expr $(,)?) => {
#[allow(unknown_lints, clippy::eq_op)]
const _: () = assert!($x);
};
}
特点
- 编译时执行:在编译期检查常量表达式
- 使用常量断言 :通过
const _: () = assert!($x)在编译期求值 - 抑制警告 :
#[allow(unknown_lints, clippy::eq_op)]避免不必要的 lint 警告 - 创建匿名常量 :
const _: ()创建一个未命名的单元类型常量
工作原理
rust
// 在编译时检查
const_assert!(std::mem::size_of::<Vec3A>() == 16); // 编译通过
const_assert!(std::mem::size_of::<Vec3A>() == 12); // 编译错误!
3. const_assert_eq! 宏 - 编译时相等断言
rust
macro_rules! const_assert_eq {
($x:expr, $y:expr $(,)?) => {
const_assert!($x == $y);
};
}
特点
- 是
const_assert!的便捷封装 - 专门用于检查两个表达式是否相等
- 接收两个参数并生成相等性断言
使用示例
rust
// 检查类型大小
const_assert_eq!(std::mem::size_of::<Vec3>(), 12);
const_assert_eq!(std::mem::size_of::<Vec3A>(), 16);
// 检查对齐要求
const_assert_eq!(std::mem::align_of::<Mat4>(), 16);
实际应用场景
1. 参数验证
rust
impl Quat {
pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
glam_assert!(axis.is_normalized()); // 仅在调试时检查
// 实现逻辑...
}
}
2. 编译时类型检查
rust
// 确保类型大小符合预期
const_assert_eq!(std::mem::size_of::<Vec3A>(), 16);
const_assert_eq!(std::mem::size_of::<Mat3A>(), 48);
3. 特性兼容性检查
rust
// 确保对齐要求
#[cfg(target_feature = "sse2")]
const_assert_eq!(std::mem::align_of::<Vec4>(), 16);
性能影响
运行时断言 (glam_assert!)
| 模式 | 性能影响 | 适用场景 |
|---|---|---|
| 启用时 | 增加运行时检查开销 | 调试、测试、开发环境 |
| 禁用时 | 零开销,完全移除 | 生产环境、发布构建 |
编译时断言 (const_assert!/const_assert_eq!)
| 特性 | 说明 |
|---|---|
| 运行时开销 | 始终为零,在编译期完成检查 |
| 二进制影响 | 不会影响生成的二进制文件大小 |
| 执行速度 | 不影响运行时执行速度 |
| 适用场景 | 类型布局验证、常量检查 |
设计优势
| 优势 | 说明 |
|---|---|
| 灵活性 | 通过特性控制断言行为,适应不同环境需求 |
| 零成本抽象 | 不启用时完全消除开销,符合 Rust 哲学 |
| 编译期安全 | 静态验证重要属性,提前发现错误 |
| 代码清晰 | 将验证逻辑与业务逻辑分离,提高可维护性 |
特性组合矩阵
debug-glam-assert |
glam-assert |
debug_assertions |
glam_assert! 行为 |
|---|---|---|---|
| ✅ | ❌ | ✅ | 启用断言 |
| ✅ | ❌ | ❌ | 禁用断言 |
| ❌ | ✅ | ✅/❌ | 启用断言 |
| ❌ | ❌ | ✅/❌ | 禁用断言 |
总结
这种断言宏的设计模式在 Rust 的底层库中很常见,具有以下特点:
- 双重控制机制:通过特性开关和编译模式控制断言
- 编译期/运行时分离:不同类型的断言满足不同需求
- 安全性与性能平衡:既保证了安全性又维持了高性能
- Rust idiomatic:符合 Rust 的零成本抽象理念
这种设计使得 glam 能够在开发过程中提供充分的调试支持,同时在生产环境中保持最佳性能。