如何实现支持两种参数格式的Rust map宏?
实现支持两种格式的
map宏 你的问题出在最初的map!宏使用$tt:tt捕获条目时,会将a: "Content of a"这类完整条目拆分为三个独立的token tree(a、:、"Content of a"),导致map_one!收到单个:或字符串时无法匹配规则,从而触发编译错误。
要同时支持简写格式(仅标识符)和完整格式(标识符: 值),可以使用递归宏逐个处理每个条目,或者直接在宏规则中匹配两种条目格式。下面是两种可行的实现方案:
方案一:递归宏实现
这种方式通过递归逐个解析每个条目,逻辑清晰且易于扩展:
// 假设你的Map结构体定义如下 #[derive(Debug)] pub struct Map<'a>(pub &'a [(String, String)]); macro_rules! map { // 空映射的情况 () => { crate::Map(&[]) }; // 单个简写条目(仅标识符) ($key:ident) => { crate::Map(&[(stringify!($key).to_string(), $key.to_string())]) }; // 单个完整条目(标识符: 值) ($key:ident : $value:expr) => { crate::Map(&[(stringify!($key).to_string(), $value.to_string())]) }; // 简写条目 + 剩余条目,递归处理剩余部分 ($key:ident, $($rest:tt)*) => { match map!($($rest)*) { crate::Map(slice) => crate::Map(&[(stringify!($key).to_string(), $key.to_string()), slice[..]][..]) } }; // 完整条目 + 剩余条目,递归处理剩余部分 ($key:ident : $value:expr, $($rest:tt)*) => { match map!($($rest)*) { crate::Map(slice) => crate::Map(&[(stringify!($key).to_string(), $value.to_string()), slice[..]][..]) } }; } // 测试代码 fn main() { let c = "Content of c"; let my_map = map![ a: "Content of a", c, d: 0xDC097397 ]; println!("{:?}", my_map); // 输出:Map(&[("a", "Content of a"), ("c", "Content of c"), ("d", "3609701271")]) }
方案二:单规则匹配两种条目格式
这种方式利用宏的可选模式匹配,在单个规则中同时支持两种条目格式,代码更紧凑:
#[derive(Debug)] pub struct Map<'a>(pub &'a [(String, String)]); macro_rules! map { // 匹配多个条目,支持两种格式,末尾允许添加可选逗号 ($($( $key:ident ) $( : $value:expr )? ),* $(,)?) => { crate::Map(&[ $( ( stringify!($key).to_string(), // 根据是否存在$value来选择对应的值 $( $value.to_string() )? $( $key.to_string() )? ) ),* ][..]) }; } // 测试代码 fn main() { let c = "Content of c"; let my_map = map![ a: "Content of a", c, d: 0xDC097397, // 末尾可选逗号 ]; println!("{:?}", my_map); // 输出:Map(&[("a", "Content of a"), ("c", "Content of c"), ("d", "3609701271")]) }
说明
- 方案一中的递归宏通过逐步拆分条目,每次处理一个条目后递归处理剩余部分,最终拼接成完整的切片。
- 方案二中的
$( : $value:expr )?表示该部分是可选的,当存在$value时使用$value.to_string(),否则使用$key.to_string()。末尾的$(,)?允许在最后一个条目后添加逗号,提升书写体验。
内容的提问来源于stack exchange,提问作者user21352310
相关产品推荐
相关产品推荐

