Rust 声明式宏中的 Metavariables 有哪些
官方文档确实写得很好,但是缺少一些风味,容易催眠😵💫
还是直接看例子更爽一些,通常我们可以从示例代码中之间看出官方文档要表达的意思,而且很多时候我们可以直接在示例代码的基础上改一改,就能满足我们自己的定制化需求。越抽象的东西,越是如此😄
item
An item is a component of a crate. Items are organized within a crate by a nested set of modules. Every crate has a single "outermost" anonymous module; all further items within the crate have paths within the module tree of the crate.
There are several kinds of items:
- modules
- extern crate declarations
- use declarations
- function definitions
- type definitions
- struct definitions
- enumeration definitions
- union definitions
- constant items
- static items
- trait definitions
- implementations
- extern blocks
可以把整个 crate 比作一个 struct,这个 struct 里面的每一个 field 都是一个 item
这些 item 包括每一个 struct
, fn
, mod
的定义...
macro_rules! test_item {
($x: item) => {
// #[derive(Debug)]
$x
}
}
test_item!(#[derive(Debug)] struct A{a: i32});
test_item!(fn foo(){println!("foo");});
test_item!(mod ma{});
let a = A{ a: 9};
println!("a: {:?}", a); // 输出 a: A { a: 9 }
foo(); // 输出 foo
block
block 是匿名的命名空间作用域(anonymous namespace scope),就是以 {
开始 以 }
结束的整个块
macro_rules! test_block {
($x: block) => {
$x
}
}
test_block!({
println!("block");
});
stmt
A statement is a component of a block, which is in turn a component of an outer expression or function
一个 stmt 可以是一个前面的 [item](## item), 或是 let statement
, 或是 expression statement
, 或是宏调用
macro_rules! test_stmt {
($x: stmt) => {
$x
}
}
// let statement
test_stmt!(let state = 8);
println!("state: {}", state);
// item
test_stmt!(#[derive(Debug)] struct B{b: i32});
// expression statement
test_stmt!{
if true {
1
} else {
2
}
};
// Macro Invocation
test_stmt!{
println!("Macro Invocation")
};
pat_param
用来 pattern match 的 param, 匹配一个 pattern, 像 match 内 =>
左边的那些东西
pat_param 默认不匹配 or-pattern
: 0 | 1 | 2
macro_rules! test_pat_param {
($x: pat_param) => {
$x
}
}
struct Person {
car: Option<String>,
age: u8,
name: String,
gender: u8,
gfs: [String; 3],
}
let person = Person {
car: Some("bmw".into()),
age: 18,
name: "hansomeboy".into(),
gender: 1,
gfs: ["Lisa".into(), "Jennie".into(), "Rosé".into()],
};
if let test_pat_param!(
Person {
car: Some(_),
age: person_age @ 13..=19,
name: ref person_name,
gfs: ref whole @ [.., ref last],
..
}
) = person {
println!("{} has a car and is {} years old, and his last GF is {}.", person_name, person_age, last);
}
macro_rules! test_or_pattern {
($($x: pat_param)*) => ();
}
test_or_pattern!{
Some(_)
Foo{x}
// error: no rules expected the token `|`
// 0 | 1 | 2
}
输出: hansomeboy has a car and is 18 years old, and his last GF is Rosé.
pat
用来匹配任意类型的 pattern, 包括 or-pattern
: 0 | 1 | 2
macro_rules! test_pat {
($($pat:pat)*) => ();
}
test_pat! {
"literal"
_
0..5
ref mut PatternsAreNice
0 | 1 | 2 | 3
}
expr
一个表达式 Expression
macro_rules! test_expr {
($($x: expr)*) => ();
}
test_expr!{
"hello" // LiteralExpression
42 // LiteralExpression
a::b::c // PathExpression
a * b + c // OperatorExpression
&a
*a
-a
a = b
a += b
a || b && c
a & b | c
(a+b) - c
["a"] // ArrayExpression
a[b][c] // IndexExpression
("world", 42) // TupleExpression
tuple.0 // TupleIndexingExpression
Point {x: 1.0, y: 2.0} // StructExpression
foo() // CallExpression
"3.14".parse() // MethodCallExpression
foo().await // AwaitExpression
bar.await
hello.world // FieldExpression
move|x,y| -> () {} // ClosureExpression
async move {} // AsyncBlockExpression
0..10 // RangeExpression
(_, a) // UnderscoreExpression
vec![1,2,3] // MacroInvocation
continue 'hello // ContinueExpression
break 'hello // BreakExpression
return 42 // ReturnExpression
{} // BlockExpression
#[cfg("hello")]{}
unsafe {} // UnsafeExpression
if condition {} // IfExpression
if let a=b {} // IfLetExpression
match x {} // MatchExpression
}
ty
一个类型 Type
macro_rules! test_ty {
($($x: ty)*) => ();
}
test_ty! {
u32
bool
char
str
String
[u8; 128]
(u32, bool)
&[u8]
Hello // User-defined types
fn(u8, u8) -> ()
&mut reference
*mut pointer
*const pointer
dyn Trait // Trait Object
impl Trait // Impl Trait
}
ident
一个标识符(用来标识变量、函数、类、对象或其他程序实体的名称) an IDENTIFIER_OR_KEYWORD or RAW_IDENTIFIER
其实就是变量名(函数名,类名,模块名...)
macro_rules! test_ident {
($($x: ident)*) => ();
}
test_ident! {
foo
_identifier
r#true // raw identifier
深圳
}
path
macro_rules! test_path {
($($x: path)*) => ();
}
test_path! {
a::b::c
self::a::<b>::c
::std::time::Instant::now()
}
tt
标记树(token tree) 是一种介于标记 (token) 与 AST(abstract syntax tree) 之间的东西
几乎所有的 token 都是 token tree 的叶子节点(leaf node), 只有被 (...)
, [...]
和 {...}
包裹在一起的一组一组的 tokens 不是叶子节点
a + b + (c + d[0]) + e
会被解析成下面的 token tree:
«a» «+» «b» «+» «( )» «+» «e»
╭────────┴──────────╮
«c» «+» «d» «[ ]»
╭─┴─╮
«0»
可以看出 token tree 的 root 节点其实是第一行的这一整组 token: «a» «+» «b» «+» «( )» «+» «e»
, 而不是单一的 node
而 AST 会生成下面这种只有一个 root 节点的数状结构:
┌─────────┐
│ BinOp │
│ op: Add │
┌╴│ lhs: ◌ │
┌─────────┐ │ │ rhs: ◌ │╶┐ ┌─────────┐
│ Var │╶┘ └─────────┘ └╴│ BinOp │
│ name: a │ │ op: Add │
└─────────┘ ┌╴│ lhs: ◌ │
┌─────────┐ │ │ rhs: ◌ │╶┐ ┌─────────┐
│ Var │╶┘ └─────────┘ └╴│ BinOp │
│ name: b │ │ op: Add │
└─────────┘ ┌╴│ lhs: ◌ │
┌─────────┐ │ │ rhs: ◌ │╶┐ ┌─────────┐
│ BinOp │╶┘ └─────────┘ └╴│ Var │
│ op: Add │ │ name: e │
┌╴│ lhs: ◌ │ └─────────┘
┌─────────┐ │ │ rhs: ◌ │╶┐ ┌─────────┐
│ Var │╶┘ └─────────┘ └╴│ Index │
│ name: c │ ┌╴│ arr: ◌ │
└─────────┘ ┌─────────┐ │ │ ind: ◌ │╶┐ ┌─────────┐
│ Var │╶┘ └─────────┘ └╴│ LitInt │
│ name: d │ │ val: 0 │
└─────────┘ └─────────┘
macro_rules! test_tt {
($($x: tt)*) => {
println!("test_tt:");
$( println!("{:?}", stringify!($x)); )*
}
}
test_tt! {
(a + b + (c + d[0]) + e)
[a + b + (c + d[0]) + e]
{a + b + (c + d[0]) + e}
}
test_tt! {
a + b + (c + d[2]) + e
}
输出:
test_tt:
"(a + b + (c + d [0]) + e)"
"[a + b + (c + d [0]) + e]"
"{ a + b + (c + d [0]) + e }"
test_tt:
"a"
"+"
"b"
"+"
"(c + d [2])"
"+"
"e"
meta
用来匹配 attribute 属性
就是那些写在 struct/enum/fn 前面的 #[...]
#![...]
macro_rules! test_meta {
($(#$(!)?[$meta: meta])*) => ();
}
test_meta! {
#[hello]
#![world]
#[a="b"]
}
lifetime
macro_rules! test_lifetime {
($($x: lifetime)*) => ();
}
test_lifetime! {
'a
'_
'static
}
vis
macro_rules! test_vis {
// ∨~~Note this comma, since we cannot repeat a `vis` fragment on its own
($($x: vis,)*) => ();
}
test_vis! {
,
pub,
pub(crate),
pub(super),
pub(self),
pub(in crate::a::b),
}
literal
macro_rules! test_literal {
($($x: literal)*) => ();
}
test_literal! {
0
b'x'
'x'
"hello"
true
}