如何将Rust固定参数log宏改写为支持任意入参的可变参数宏
Rust 实现支持任意数量入参的
log!声明宏 现有实现
你当前编写的log!宏仅支持0-3个入参,逻辑是将所有传入参数按顺序拼接为字符串后调用info!输出日志,代码如下:
//Amazing log function that can take up to three parameters!!! macro_rules! log { // todo variadic () => { let string = " "; info!(" "); }; ($a:expr) => { let string = format!("{}", format_args!("{}", $a)); info!("{}", string); }; ($a:expr,$b:expr) => { let string = format!("{}", format_args!("{}{}", $a, $b)); info!("{}", string); }; ($a:expr,$b:expr,$c:expr) => { let string = format!("{}", format_args!("{}{}{}", $a, $b, $c)); info!("{}", string); }; }
调用示例:
let number = 163.19; log!("Heeey fam is this number right: ", number, ". No, it's wrong"); // 输出: [TIME INFO project] Heeey fam is this number right: 163.19. No, it's wrong
可变参数实现方案
Rust 声明宏macro_rules!原生支持可变参数匹配,不需要为每个参数数量单独编写分支,使用$(...),*/$(...),+的重复匹配模式即可支持任意数量的入参。
与原逻辑完全一致的实现
以下实现完全保留你原来「所有参数直接按Display规则拼接输出」的逻辑,同时支持任意数量参数,还去掉了原代码里多余的中间字符串分配,性能更好:
macro_rules! log { // 0个参数的分支,和原有逻辑保持一致 () => { info!(" "); }; // 匹配1个及以上逗号分隔的表达式,末尾$(,)?支持最后一个参数后加逗号的Rust常规写法 ($($arg:expr),+ $(,)?) => { // 编译期自动生成和参数数量一致的"{}{}...{}"格式串,直接传给info!,无额外运行时开销 info!(concat!($("{}"),+), $($arg),+); }; }
规则说明
$($arg:expr),+ $(,)?是声明宏的重复匹配语法:$arg:expr表示匹配单个表达式类型的参数,绑定到$arg变量- 括号后的
,表示参数之间用逗号分隔 - 末尾的
+表示该模式匹配至少1次,对应1到任意多个参数;如果需要匹配0次及以上可以替换为* - 最后的
$(,)?表示允许参数列表末尾加可选的逗号,符合Rust日常编码习惯
- 展开时
concat!($("{}"),+)会在编译期拼接出和参数数量匹配的格式化字符串(比如3个参数就会生成"{}{}{}"),之后$($arg),+把所有捕获的参数按原顺序展开传入,和你手动写固定数量参数分支的行为完全一致。
扩展:对齐标准info!的格式化用法
如果你不需要强制把所有参数直接拼接,想让宏支持和标准库日志宏一样的格式化占位符写法,可以直接用透传版本,实现更简洁:
macro_rules! log { // 透传所有输入参数给info!,支持所有标准格式化语法 ($($arg:tt)*) => { info!($($arg)*); }; }
这个版本既支持你原来的多参数用法,也支持log!("数值: {}, 字符串: {}", 123, "test")这类带占位符的调用,和tracing/log crate的info!宏行为完全对齐。
内容的提问来源于stack exchange,提问作者Bron
相关产品推荐
相关产品推荐

