元变量表达式
RFC: rfcs#1584
Tracking Issue: rust#83527
Feature:#![feature(macro_metavar_expr)]
注意:示例代码片段非常简单,只试图展示它们是如何工作的。
关于这些元变量表达式,如果你认为你有合适的、单独使用的小片段,请提交它们!
正如在 思路 中提到的,Rust 有一些特殊的元变量表达式(以下简称表达式):transcriber1 可以使用这些表达式来获取有关元变量的信息。如果没有这些表达式,它们所提供的信息就很难甚至不可能获得。
本章将结合用例对它们进行更深入的介绍。
译者注:在专业的讨论中,尤其涉及元变量表达式,常用 transcribe(r) 一词而不使用 expand (expansion)。
Dollar Dollar ($$
)
$$
表达式展开为单个 $
,实际上使其成为转义的 $
。这让声明宏宏生成新的声明宏。
因为以前的声明宏将无法转义 $
,所以无法使用元变量、重复和元变量表达式。例如以下代码片段中不使用 $$
,就无法使用 bar!
:
macro_rules! foo { () => { macro_rules! bar { ( $( $any:tt )* ) => { $( $any )* }; // ^^^^^^^^^^^ error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth } }; } foo!(); fn main() {}
问题很明显, foo!
的 transcriber 看到有反复捕获的意图,并试图反复捕获,但它的作用域中没有 $any
元变量,这导致它产生错误。有了 $$
,我们就可以解决这个问题2,因为 foo
的 transcriber 不再尝试反复捕获。
#![feature(macro_metavar_expr)] macro_rules! foo { () => { macro_rules! bar { ( $$( $$any:tt )* ) => { $$( $$any )* }; } }; } foo!(); bar!(); fn main() {}
译者注:在没有 $$
之前,存在一种技巧绕过这里的问题:你可以使用 $tt
捕获 $
来进行转义,比如这样。
count($ident, depth)
count
表达式展开成元变量 $ident
在给定反复深度的反复次数。
ident
参数必须是规则作用域中声明的元变量depth
参数必须是值小于或等于元变量$ident
出现的最大反复深度的整型字面值count(ident, depth)
展开成不带后缀的整型字面值标记count(ident)
是count(ident, 0)
的简写
#![feature(macro_metavar_expr)] macro_rules! foo { ( $( $outer:ident ( $( $inner:ident ),* ) ; )* ) => { println!("count(outer, 0): $outer repeats {} times", ${count($outer)}); println!("count(inner, 0): The $inner repetition repeats {} times in the outer repetition", ${count($inner, 0)}); println!("count(inner, 1): $inner repeats {} times in the inner repetitions", ${count($inner, 1)}); }; } fn main() { foo! { outer () ; outer ( inner , inner ) ; outer () ; outer ( inner ) ; }; }
index(depth)
index(depth)
表达式展开为给定反复深度下,当前的迭代索引。
depth
参数表明在第几层反复,这个数字从最内层反复调用表达式开始向外计算index(depth)
展开成不带后缀的整型字面值标记index()
是index(0)
的简写
#![feature(macro_metavar_expr)] macro_rules! attach_iteration_counts { ( $( ( $( $inner:ident ),* ) ; )* ) => { ( $( $(( stringify!($inner), ${index(1)}, // 这指的是外层反复 ${index()} // 这指的是内层反复,等价于 `index(0)` ),)* )* ) }; } fn main() { let v = attach_iteration_counts! { ( hello ) ; ( indices , of ) ; () ; ( these, repetitions ) ; }; println!("{v:?}"); }
len(depth)
length(depth)
表达式展开为在给定反复深度的迭代次数。
depth
参数表示在第几层反复,这个数字从最内层反复调用表达式开始向外计算length(depth)
展开成不带后缀的整型字面值标记length()
是length(0)
的简写
#![feature(macro_metavar_expr)] macro_rules! lets_count { ( $( $outer:ident ( $( $inner:ident ),* ) ; )* ) => { $( $( println!( "'{}' in inner iteration {}/{} with '{}' in outer iteration {}/{} ", stringify!($inner), ${index()}, ${len()}, stringify!($outer), ${index(1)}, ${len(1)}, ); )* )* }; } fn main() { lets_count!( many (small , things) ; none () ; exactly ( one ) ; ); }
ignore(ident)
ignore(ident)
表达式展开为空,这使得在无需实际展开元变量的时候,像元变量反复展开相同次数的某些内容。
ident
参数必须是规则作用域中声明的元变量
#![feature(macro_metavar_expr)] macro_rules! repetition_tuples { ( $( ( $( $inner:ident ),* ) ; )* ) => { ($( $( ( ${index()}, ${index(1)} ${ignore($inner)} // without this metavariable expression, compilation would fail ), )* )*) }; } fn main() { let tuple = repetition_tuples!( ( one, two ) ; () ; ( one ) ; ( one, two, three ) ; ); println!("{tuple:?}"); }