08-macro_rules!宏的标记类型
9.6 macro_rules!宏的标记类型
在我们构建更复杂的宏之前,熟悉macro_rules!宏的有效输入类型将非常重要。因为macro_rules!宏是在语法层面工作,它需要为用户提供这些句法元素的句柄,并区分宏可以包含哪些内容,以及如何与它们进行交互。
以下是一些重要的标记树类型,你可以将它们作为输入传递给宏。
- 代码块:这是一系列语句。我们在演示调试程序时已经使用过代码块。它匹配由花括号分隔的任何语句序列,例如我们之前使用的内容:
{ silly; things; }
该代码块包含两条语句,分别是“silly;”和“things;”。
- expr:匹配任意表达式。如下。
- 1。
- x + 1。
- if x == 4 { 1 } else { 2 }。
- ident:匹配一个标识符。标识符是任何不是关键字(例如if和let)的Unicode字符串。此外,单独的下画线字符在Rust中不是标识符。标识符的示例如下所示。
- x。
- long_identifier。
- SomeSortOfAStructType。
- item:匹配元素,模块级的内容可以被当作元素。它包括函数、use声明及类型定义等。以下是一些示例。
- use std::io;。
- fn main() { println!("hello") }。
- const X: usize = 8;。
当然,这些内容不一定是单行的代码。main函数可以是单个元素,即使它包含多行代码。
- meta:表示一个元项目。属性内的参数被称为元项,由元捕获。属性本身如下所示。
-
![foo]。
-
[baz]。
-
[foo(bar)]。
-
[foo(bar="baz")]。
-
- 元项是在括号内找到的内容。因此,对于前面的每个属性,相应的元项如下所示:
- foo。
- baz。
- foo(baz)。
- foo(bar="baz")。
- pat:这是一种模式。每个match表达式中的左侧都是模式,它们由pat捕获。这里有一些示例。
- 1。
- "x"。
- t。
- *t。
- Some(t)。
- 1 | 2 | 3。
- 1 … 3。
- _。
- path:匹配限定名称。路径是限定名称,即附加了命名空间的名称。它们与标识符非常相似,只是它们在名称中允许使用双冒号,因为这表示路径。以下是一些示例。
- foo。
- foo::bar。
- Foo。
- Foo::Bar::baz。
这是你需要捕获某种类型的路径,在后续生成的代码中使用它时会非常有用,例如使用路径对复杂类型进行别名化。
- stmt:这表示一条语句。stmt除了能够接收更多模式以外,它和表达式类似。以下是一些示例。
- let x = 1。
- 1。
- foo。
- 1+2。
与第1个示例相反,expr不会接收let x = 1这样的语句。
- tt:这是一个标记树,它由一系列其他标记构成。关键字tt会捕获单个标记树。标记树既可以由单个标记(例如1、+、或者foo bar)构成,也可以由()、[]、{}等符号包围的一系列标记构成。以下是一些示例。
- foo。
- { bar; if x == 2 { 3 } else { 4 }; baz }。
- { bar; if x == 2 ( 3 ) ulse } 4 {; baz }。
如你所见,标记树的内部包含的并不一定是具有语义的内容,只需要是一系列标记即可。具体而言,与此不匹配的是两个或多个未用括号括起来的标记(例如1+2)。这是macro_rules!可以捕获的最常见的代码或标记序列。
- ty:这表示一个Rust类型。关键字ty会捕获看起来像类型的东西。以下是一些示例。
- u32。
- u33。
- String。
类型在宏展开阶段不需要进行语义检查,因此“u33”和“u32”一样可以接受。但是,一旦代码生成并进入语义分析阶段,就会对类型进行检查,给出错误提示信息:“error: expected type, found 'u33'”。当生成用于创建函数的代码或在类型上实现特征的方法时,将使用此方法。
- vis:这表示可见性修饰符,它会捕获可见性修饰符pub、pub(crate)等。当生成模块级代码并需要捕获已传递给宏的代码片段中的可见性修饰符时,这将非常有用。
- lifetime:表示生命周期,例如'a、'ctx、'foo等。
- literal:可以是任何标记的文字,如字符串文字(例如"foo")或标识符(例如bar)。