Rust MacroMatch能否使用逗号以外的分隔符?使用>>作为分隔符的宏编译失败原因探究
为什么用>>作为宏分隔符会编译失败,以及如何使用非逗号分隔符定义宏
问题核心原因
你遇到的编译错误,本质上是宏片段指定符(比如expr)有自己的语法上下文限制,和MacroRepSep的通用规则不是一回事。
虽然根据语法定义,>>属于合法的Token,理论上可以作为MacroRepSep,但expr这类片段指定符要求后续的Token不能和Rust自身的表达式语法产生歧义。对于expr来说,>>是右移运算符的一部分——比如1>>2本身就是一个合法的表达式,宏匹配时会尽可能把最长的合法表达式匹配给$a:expr,这就导致编译器无法把>>识别成分隔符,反而认为你在一个完整的表达式后面非法追加了>>,所以抛出错误:“$a:expr is followed by >>, which is not allowed for expr fragments”。
能不能用逗号以外的分隔符?
当然可以!只要你选择的分隔符是对应片段指定符允许的后续Token,不会和该片段的语法产生冲突。举几个可行的例子:
示例1:用分号作为分隔符
macro_rules! add_list { ($($a:expr);*) => { 0 $(+$a)* } } pub fn main() { println!("{}", add_list!(1; 2; 3)); // 输出 6 }
示例2:用竖线作为分隔符
macro_rules! add_list { ($($a:expr)|*) => { 0 $(+$a)* } } pub fn main() { println!("{}", add_list!(1|2|3)); // 输出 6 }
特殊需求:用类似>>的符号怎么办?
如果一定要用这类容易产生歧义的符号作为分隔符,你可以换用限制更少的片段指定符——比如tt(Token Tree)。tt会匹配任意单个Token Tree,不会像expr那样严格遵循表达式语法,因此可以避开歧义问题:
macro_rules! add_list2 { ($($a:tt)>>*) => { 0 $(+$a)* } } pub fn main() { println!("{}", add_list2!(1>>2>>3)); // 输出 6 }
不过要注意,使用tt会失去expr带来的类型检查优势——如果传入非表达式内容,错误会在宏展开后才暴露,而不是在匹配阶段就提示。
内容的提问来源于stack exchange,提问作者calvin
相关产品推荐
相关产品推荐

