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

如何正确向schema_for!宏传递含双冒号的路径?宏片段符是否有误?

Rust宏中传递含双冒号路径给schemars::schema_for!的错误解决

问题场景

在使用schemars库生成API schema时,需要批量创建CommandDescription结构体实例,原始代码存在大量重复:

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CommandDescription {
    /// 命令/操作在JSON中的名称
    pub cmd: String,
    /// 命令接受的参数
    pub parameters: Option<RootSchema>,
}

重复实例示例:

CommandDescription {
    cmd: String::from(api_schema::about::ABOUT_CMD),
    parameters: Some(schema_for!(api_schema::about::AboutCmd)),
},

为简化代码,编写了如下宏:

macro_rules! command_description {
    ($api_path:path, $prefix:ident, $type_name_cmd:ident) => {
        CommandDescription {
            cmd: String::from(concat!(
                stringify!($api_path),
                "::",
                stringify!($prefix),
                "_CMD"
            )),
            parameters: Some(schema_for!($api_path::$type_name_cmd)),
        }
    };
}

调用方式:

command_description!(
    api_schema::about,
    ABOUT,
    AboutCmd
)

但编译器报错:

error: missing angle brackets in associated item path
   --> src/bin/rudi-service.rs:674:42
    |
674 |               parameters: Some(schema_for!($api_path::$type_name_cmd)),
    |                                            ^^^^^^^^^
    = note: this error originates in the macro `command_description` (in Nightly builds, run with -Z macro-backtrace for more info)
help: types that don't start with an identifier need to be surrounded with angle brackets in qualified paths
    |
674 |             parameters: Some(schema_for!(<$api_path>::$type_name_cmd)),
    |                                          +         +

添加尖括号后,编译器又提示$api_path不是类型,陷入矛盾。

解决方案

方案1:直接传递完整路径(推荐)

放弃拆分路径的写法,直接将类型的完整路径和常量的完整路径作为宏参数,避免路径拼接的解析歧义:

macro_rules! command_description {
    ($full_type:path, $cmd_const:path) => {
        CommandDescription {
            cmd: String::from($cmd_const),
            parameters: Some(schema_for!($full_type)),
        }
    };
}

调用方式:

command_description!(api_schema::about::AboutCmd, api_schema::about::ABOUT_CMD)

这种写法更简洁,也避开了宏中路径拼接的解析问题,同时减少了字符串拼接的开销(直接引用常量而非拼接字符串)。

方案2:使用tt片段符处理拆分路径

如果坚持要拆分路径前缀、常量前缀和类型名,可以使用tt(Token Tree)片段符,它能更灵活地处理带双冒号的路径,避免编译器的解析歧义:

macro_rules! command_description {
    ($($api_path:tt)::*, $prefix:ident, $type_name_cmd:ident) => {
        CommandDescription {
            cmd: String::from(concat!(
                $(stringify!($api_path), "::",)*
                stringify!($prefix), "_CMD"
            )),
            parameters: Some(schema_for!($($api_path)::*::$type_name_cmd)),
        }
    };
}

调用方式保持不变:

command_description!(api_schema::about, ABOUT, AboutCmd)

原理说明

原来的$api_path:path片段符会让编译器将$api_path视为一个完整的路径项,当拼接$api_path::$type_name_cmd时,编译器会错误地将其解析为关联项路径(比如类型的关联类型/函数),而非模块下的类型路径。而tt片段符只是将传入的token序列原样展开,不会触发这种解析歧义,编译器能正确识别为模块路径下的类型。

内容的提问来源于stack exchange,提问作者Jamie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 17:04:53