一、宏系统概述
(一)、 核心功能
storage_types! 宏系统是一个高级代码生成工具,主要功能包括:
-
多类型代码生成:为不同数值类型自动生成重复代码结构
-
类型分类支持:内置 7 种类型分类
分类名 | 包含类型 |
---|---|
All | 所有支持的类型(基本整数、大数、有理数、复数、浮点数) |
PrimInt | 基本整数类型(usize, u8-i128) |
Ratio | 有理数类型(Rational, Rational32, Rational64, BigRational) |
Float | 浮点类型(f32, f64) |
Signed | 有符号类型(isize, i8-i128, BigInt, 有理数, 浮点数) |
Unsigned | 无符号类型(usize, u8-u128, BigUint) |
Complex | 复数类型(Complex32, Complex64) |
-
模块化组织:为每种类型生成独立模块
-
条件编译:通过 feature 标志控制类型包含
(二)、设计优势
-
减少重复代码:避免为每种类型手动编写相似代码
-
统一接口:为不同数值类型提供一致的使用方式
-
灵活扩展:易于添加新类型或类型分类
-
编译优化:未启用的类型完全不生成代码
二、宏定义详解
(一)、文档与条件编译
#[cfg_attr(all(feature = "f32", feature = "f64"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "f32", feature = "f64")), doc = " ```rust,ignore")]
-
作用:根据 feature 组合控制文档示例的编译行为
-
逻辑:
-
同时启用 f32 和 f64:文档示例可编译
-
否则:文档示例仅显示不编译
-
(二)、主宏结构(storage_types!)
#[macro_export]
macro_rules! storage_types {
// 公开/非公开模块入口
($(#[$attr:meta])* types: $($T:ident),+; $($tt:tt)*) => {
... };
($(#[$attr:meta])* pub types: $($T:ident),+; $($tt:tt)*) => {
... };
// 类型分发处理器
(@types $attr:tt @$M:ident $($T:ident),+; $tt:tt) => {
... };
// 具体类型处理器(示例)
(@type ($(#[$attr:meta])*) @$M:ident usize ($($tt:tt)*)) => {
... };
// 类型分类处理器(示例)
(@type ($(#[$attr:meta])*) @$M:ident Float ($($tt:tt)*)) => {
... };
// 模块生成器
(@mod ($(#[$attr:meta])*) $M:ident, $V:ty; ($($tt:tt)*)) => {
... };
(@pub_mod ($(#[$attr:meta])*) $M:ident, $V:ty; ($($tt:tt)*)) => {
... };
// 默认处理
($($tt:tt)*) => {
... };
}
第一匹配项详解
($(#[$attr:meta])* types: $($T:ident),+; $($tt:tt)*) => {
storage_types!(@types ($(#[$attr])*) @mod $($T),+; ($($tt)*));
};
- 输入模式:
-
$(#[$attr:meta])*
:- 匹配零个或多个属性(attributes),如 #[derive(Debug)] 或 #[allow(dead_code)]。
- 如果输入是 #[derive(Debug)] types: u32, f64; …,那么 KaTeX parse error: Expected 'EOF', got '#' at position 2: (#̲[attr])* 会捕获 #[derive(Debug)]。
-
types
::字面匹配,必须是 types: 这个标识符 -
$($T:ident),+
:- 匹配一个或多个由逗号分隔的标识符(如 u32, f64 或 All)
- 例如,u32、u32, f64 或 All 都是有效的。
-
;
:字面分号 -
$($tt:tt)*
:- 匹配任意数量的 token tree(即任意 Rust 代码)
- 这部分内容会被原样传递到生成的模块中。
- 展开规则:
- 调用宏自身的 @types 内部规则(宏的内部转发规则,用于进一步处理),并传递以下参数:
-
($(#[$attr])*)
:捕获的属性 -
@mod
:字面标记,表示生成的模块默认是私有的- 如果是 pub types: …,则会使用 @pub_mod 生成公开模块。
-
$($T),+
:捕获的类型标识符 -
($($tt)*)
:捕获的其余 token tree(作为额外的模块内容)
-
- 示例展开
假设有以下宏调用:
storage_types! {
#[derive(Debug, Clone)]
types: u32, f64;
const EXAMPLE: V = 42;
}
它会展开为:
storage_types!(@types (#[derive(Debug, Clone)]) @mod u32, f64; (
const EXAMPLE: V = 42;
));
然后 @types 规则会进一步处理,为 u32 和 f64 分别生成模块,并将 const EXAMPLE: V = 42; 放入每个模块中。
- 总结
这段规则的作用是:
-
匹配带有可选属性、types: 关键字、类型列表和额外代码的输入。
-
将输入转发给 @types 内部规则,并标记为生成非公开模块(@mod)。
-
最终会为每个类型生成一个模块,包含指定的额外代码。
第二匹配项详解
($(#[$attr:meta])* pub types: $($T:ident),+; $($tt:tt)*) => {
storage_types!(@types ($(#[$attr])*) @pub_mod $($T),+; ($($tt)*));
};
- pub types::字面匹配,必须是 pub types:(注意 pub 关键字)。
- @pub_mod:字面标记,表示生成的模块是公开的(pub mod)。
- 其它内容与上一个匹配相同
第三匹配项详解
(@types $attr:tt @$M:ident $($T:ident),+; $tt:tt) => {
$(storage_types!(@type $attr @$M $T $tt);)+
};
- 输入模式:
-
@types
:表示这是一个内部规则,由宏的其他规则调用。 -
$attr:tt
:匹配一个 token tree(通常是从外层传递过来的属性,如 #[derive(Debug)])。 -
@$M:ident
:匹配一个标识符(如 @mod 或 @pub_mod),表示模块的可见性。 -
$($T:ident),+
:匹配一个或多个由逗号分隔的类型标识符(如 u32, f64)。 -
$tt:tt
:匹配一个 token tree(通常是模块的额外内容,如函数、常量等)。
- 展开规则:
对每个类型 $T,调用 @type 规则:
-
$attr
:传递属性。 -
@$M
:传递模块可见性标记(@mod 或 @pub_mod)。 -
$T
:传递当前类型。 -
$tt
:传递额外内容。
- 关键点说明
$(…);+ 语法:
-
这是 Rust 宏的重复展开语法,表示对 ( ( (T),+ 中的每个 $T 执行一次 storage_types!(@type …)。
-
; 表示每次展开后用分号分隔(虽然在这个上下文中分号是可选的,因为宏调用本身不需要分号)。
-
@$M:ident 的作用:
-
$M 可以是 mod 或 pub_mod,决定生成的模块是否是公开的。
-
例如:
-
如果 @$M 是 @mod,生成的模块是 mod(私有)。
-
如果 @$M 是 @pub_mod,生成的模块是 pub mod(公开)。
-
-
-
$attr 和 $tt 的传递:
-
$attr 是属性(如 #[derive(Debug)]),会原样传递给 @type 规则。
-
$tt 是模块的额外内容(如函数、常量等),也会原样传递。
-
- 示例展开
假设有以下调用:
storage_types!(@types (#[derive(Debug)]) @mod u32, f64; (
const EXAMPLE: V = 42;
));
它会展开为:
storage_types!(@type (#[derive(Debug)]) @mod u32 (
const EXAMPLE: V = 42;
));
storage_types!(@type (#[derive(Debug)]) @mod f64 (
const EXAMPLE: V = 42;
));
然后 @type 规则会进一步处理 u32 和 f64,生成对应的模块。
- 总结
这段规则的作用是:
-
接收一组类型(如 u32, f64)和模块内容。
-
对每个类型调用 @type 规则,分别生成对应的模块。
-
确保属性和模块内容正确地传递到每个类型的处理中。
它是宏的“分发器”,将批量类型处理拆分为单个类型的处理,避免重复代码。
第四匹配项详解
(@type ($(#[$attr:meta])*) @$M:ident usize ($($tt:tt)*)) => {
storage_type_usize!(($(#[$attr])*) @$M ($($tt)*));
};
- 输入模式:
-
@type
:表示这是一个内部规则。 -
($(#[$attr:meta])*)
:匹配零个或多个属性(如 #[derive(Debug)])。 -
@$M:ident
:匹配模块可见性标记(@mod 或 @pub_mod)。 -
usize
:字面匹配,表示当前处理的类型是 usize。 -
($($tt:tt)*)
:匹配任意数量的 token tree(模块的额外内容)。
- 展开规则:
调用 storage_type_usize! 宏,并传递:
-
($(#[$attr])*)
:所有属性。 -
@$M
:模块可见性标记。 -
($($tt)*)
:模块的额外内容。
- 关键点说明
-
usize 的特化处理:
-
这是针对 usize 类型的特化规则,类似的规则也存在于其他类型(如 u32、f64 等)。
-
这种设计允许为不同类型定制不同的生成逻辑。
-
-
委托给 storage_type_usize!:
-
实际工作委托给专门的宏 storage_type_usize!,可能是为了:
-
代码模块化。
-
允许用户自定义特定类型的实现。
-
-
-
属性和内容的传递:
- 所有属性和模块内容都原样传递给 storage_type_usize!。
- 示例展开
假设有以下调用:
storage_types!(@type (#[derive(Debug)]) @pub_mod usize (
const MAX: V = usize::MAX;
));
它会展开为:
storage_type_usize!((#[derive(Debug)]) @pub_mod (
const MAX: V = usize::MAX;
));
然后 storage_type_usize! 会进一步处理,生成类似以下的代码:
#[derive(Debug)]
pub mod usize {
pub type V = usize;
const MAX: V = usize::MAX;
}
- 为什么需要这种设计?
-
关注点分离:
-
storage_types! 负责类型的分发。
-
storage_type_*! 负责具体类型的实现。
-
-
可扩展性:
- 可以单独修改某个类型的生成逻辑(如 storage_type_usize!),而不影响其他类型。
-
减少重复代码:
- 共用属性处理和模块生成的逻辑。
- 总结
这段规则的作用是:
-
捕获 usize 类型的定义请求。
-
将属性和模块内容委托给专门的 storage_type_usize! 宏处理。
-
最终生成包含指定内容和属性的 usize 模块。
这是典型的宏分派模式,在复杂宏设计中很常见。
第五匹配项详解
(@type ($(#[$attr:meta])*) @$M:ident u8 ($($tt:tt)*)) => {
storage_type_u8!(($(#[$attr])*) @$M ($($tt)*));
};
(@type ($(#[$attr:meta])*) @$M:ident u16 ($($tt:tt)*)) => {
storage_type_u16!(($(#[$attr])*) @$M ($($tt)*));
};
(@type ($(#[$attr:meta])*) @$M:ident u32 ($($tt:tt)*)) => {
storage_type_u32!(($(#[$attr])*) @$M ($($tt)*));
};
(@type ($(#[$attr:meta])*) @$M:ident u64 ($($tt:tt)*)) => {
storage_type_u64!(($(#[$attr])*) @$M ($($tt)*));
};
(@type ($(#[$attr:meta])*) @$M:ident