如何正确向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
相关产品推荐
相关产品推荐

