You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现支持两种参数格式的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 03:24:56