沈阳住房城乡建设部网站,关键词投放,国外比较好的设计网站,汽配信息门户网站模板宏看起来和函数很像#xff0c;只不过名称末尾有一个感叹号 ! 。 宏并不产生函数调用#xff0c;而是展开成源码#xff0c;并和程序的其余部分一起被编译。 Rust宏和C不同#xff0c;Rust的宏会展开为抽象语法树#xff08;AST#xff0c;abstract syntax tree#xff…宏看起来和函数很像只不过名称末尾有一个感叹号 ! 。 宏并不产生函数调用而是展开成源码并和程序的其余部分一起被编译。 Rust宏和C不同Rust的宏会展开为抽象语法树ASTabstract syntax tree而不是直接字符串替换这样就不会产生无法预料的优先级错误。
宏有两种声明宏和过程宏
一、声明宏
一定义 声明宏通过macro_rules! 创建 每个声明宏都有一个名称和一条或多条规则。 每条规则都有两部分一个匹配器(matcher)描述它匹配的句法一个转换器(transcriber)描述成功匹配后将替代调用的句法。 匹配器和转换器都必须由定界符(delimiter)包围。 宏可以替换为表达式、语句、程序项、类型、模式
1.语法格式
macro_rules! macro_name {// 匹配器和转换器(pattern1) { /* code1 */ };(pattern2) { /* code2 */ };...
}其中macro_name是宏的名字pattern是匹配器要匹配的模式code是转换器需要替换成的代码。可以看出跟match非常相似。
2.宏参数 在匹配器中使用$ IDENTIFIER : MacroFragSpec捕获宏参数 在转换器中使用$ IDENTIFIER表示宏参数
MacroFragSpec叫做指示符表示参数的类型 有下列这些指示符 item条目 例如函数、结构体、模块等 block代码块 stmt语句(statement) pat模式(pattern) expr表达式 ty类型(type) 例如u8 u16 ident标识符 例如结构体、函数的名字 path路径 例如::std::mem::replace meta元条目 例如 #[…] #![…] tt标记树 token tree vis可见性描述符 例如pub literal 用于字面量 meta 元信息例如 #[…]和 #![rust macro…] 属性
例子
macro_rules! create_function {// 此宏接受一个 ident 类型的参数并创建一个名为 $func_name 的函数。($func_name:ident) (fn $func_name() {// stringify! 宏把 ident 转换成字符串。println!(You called {:?}(), stringify!($func_name))})
}
// 借助上述宏来创建名为 foo 和 bar 的函数。
create_function!(foo);
create_function!(bar);
macro_rules! print_result {// 此宏接受一个 expr 类型的表达式并将它作为字符串连同其结果一起// 打印出来。// expr 指示符表示表达式。($expression:expr) (// stringify! 把表达式*原样*转换成一个字符串。println!({:?} {:?}, stringify!($expression), $expression))
}
fn main() {foo();bar();print_result!(1u32 1);print_result!({let x 1u32;x * x 2 * x - 1});
}3.宏重载 宏可以重载从而接受不同的参数组合。也就是说宏可以有多条规则。 类似于match代码块
例子
// 根据你调用它的方式test! 将以不同的方式来比较 $left 和 $right。
macro_rules! test {// 参数不需要使用逗号隔开。// 参数可以任意组合($left:expr; and $right:expr) (println!({:?} and {:?} is {:?},stringify!($left),stringify!($right),$left $right));($left:expr; or $right:expr) (println!({:?} or {:?} is {:?},stringify!($left),stringify!($right),$left || $right));
}
fn main() {test!(1i32 1 2i32; and 2i32 * 2 4i32);test!(true; or false);
}4.参数重复 在匹配器中使用 来表示可能出现一个或多个参数使用 * 来表示出现零个或多个参数用于可变参数列表。
* — 表示任意数量的重复元。— 表示至少有一个重复元。
? — 表示一个可选的匹配段可以出现零次或一次例子
// min! 将求出任意数量的参数的最小值。
macro_rules! find_min {// 基本情形($x:expr) ($x);// $x 后面跟着至少一个 $y,($x:expr, $($y:expr),) (// 对 $x 后面的 $y 们调用 find_min!std::cmp::min($x, find_min!($($y),)))
}
fn main() {println!({}, find_min!(1u32));println!({}, find_min!(1u32 2 , 2u32));println!({}, find_min!(5u32, 2u32 * 3, 4u32));
}5.作用域 #[macro_export] 表示只要导入了定义这个宏的 crate该宏就应该是可用的。如果没有该属性这个宏就不能被引入
例子
#[macro_export]
macro_rules! vec {( $( $x:expr ),* ) {{let mut temp_vec Vec::new();$(temp_vec.push($x);)*temp_vec}};
}二使用 宏调用是在编译时执行宏用执行结果替换该调用。 通过宏名后跟一个!和一个参数列表调用宏 比如
some_extension!(...)
some_extension!{...}
some_extension![...]例子
// 作为表达式使用
let x vec![1,2,3];
// 作为语句使用
println!(Hello!);
// 在模式中使用
macro_rules! pat {($i:ident) (Some($i))
}
if let pat!(x) Some(1) {assert_eq!(x, 1);
}
// 在类型中使用
macro_rules! Tuple {{ $A:ty, $B:ty } { ($A, $B) };
}
type N2 Tuple!(i32, i32);
// 作为程序项使用
thread_local!(static FOO: RefCellu32 RefCell::new(1));
// 作为关联程序项使用
macro_rules! const_maker {($t:ty, $v:tt) { const CONST: $t $v; };
}
trait T {const_maker!{i32, 7}
}
// 宏内调用宏
macro_rules! example {() { println!(Macro call in a macro!) };
}
// 外部宏 example 展开后, 内部宏 println 才会展开.
example!();二、过程宏
过程宏procedural macros更像函数。过程宏接收一些 代码然后产生另一些代码而非像声明式宏那样匹配对应模式然后以另一部分代码替换当前代码。 过程宏在编译时运行 过程宏必须在 crate类型为 proc-macro 的crate中定义。这种类型的crate总是链接编译器提供的 proc_macro crate。proc_macro crate提供了编写过程宏所需的各种类型和工具来让编写更容易。此crate主要包含了一个 TokenStream 类型。 注意: 使用Cargo时定义过程宏的crate的配置文件里要做如下设置
[lib]
proc-macro true过程宏有三种 一类函数过程宏 类函数过程宏是使用宏调用运算符!调用的过程宏。 这种宏是由一个带有 proc_macro属性和 (TokenStream) - TokenStream签名的 公有可见性函数定义。 输入 TokenStream 是由宏调用的定界符界定的内容输出 TokenStream 将替换整个宏调用。 例如下面的宏定义忽略它的输入并将函数 answer 输出到它的作用域
#![crate_type proc-macro]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro]
pub fn make_answer(_item: TokenStream) - TokenStream {fn answer() - u32 { 42 }.parse().unwrap()
}
然后我们用它在一个二进制crate里打印 “42” 到标准输出。
extern crate proc_macro_examples;
use proc_macro_examples::make_answer;
make_answer!();
fn main() {println!({}, answer());
}二派生宏 派生宏为派生(derive)属性定义新输入。这类宏在给定输入结构体(struct)、枚举(enum)或联合体(union) token流的情况下创建新程序项。它们也可以定义派生宏辅助属性。 派生宏由带有 proc_macro_derive属性和 (TokenStream) - TokenStream签名的公有可见性函数定义。 输入TokenStream 是带有 derive 属性的程序项的token流。输出TokenStream必须是一组程序项然后将这组程序项追加到输入TokenStream中的那条程序项所在的模块或块中。 下面是派生宏的一个示例。它没有对输入执行任何有用的操作只是追加了一个函数 answer。
#![crate_type proc-macro]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(AnswerFn)]
pub fn derive_answer_fn(_item: TokenStream) - TokenStream {fn answer() - u32 { 42 }.parse().unwrap()
}然后使用这个派生宏
extern crate proc_macro_examples;
use proc_macro_examples::AnswerFn;
#[derive(AnswerFn)]
struct Struct;
fn main() {assert_eq!(42, answer());
} 派生宏辅助属性 派生宏可以将额外的属性添加到它们所在的程序项的作用域中。这些属性被称为派生宏辅助属性。这些属性是惰性的它们存在的唯一目的是将这些属性在使用现场获得的属性值反向输入到定义它们的派生宏中。也就是说所有该宏的宏应用都可以看到它们。 定义辅助属性的方法是在 proc_macro_derive 宏中放置一个 attributes 键此键带有一个使用逗号分隔的标识符列表这些标识符是辅助属性的名称。 例如下面的派生宏定义了一个辅助属性 helper但最终没有用它做任何事情。
#![crate_typeproc-macro]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(HelperAttr, attributes(helper))]
pub fn derive_helper_attr(_item: TokenStream) - TokenStream {TokenStream::new()
}
然后在一个结构体上使用这个派生宏
#[derive(HelperAttr)]
struct Struct {#[helper]field: ()
}三属性宏 属性宏用于自定义属性。虽然rust有许多内置属性但有时我们需要自定义属性这就是宏属性。 属性宏由带有 proc_macro_attribute属性和 (TokenStream, TokenStream) - TokenStream签名的公有可见性函数定义。 签名中的第一个 TokenStream 是属性名称后面的定界token树(delimited token tree)不包括外层定界符。如果该属性作为裸属性(bare attribute)给出则第一个 TokenStream 值为空。第二个 TokenStream 是程序项的其余部分包括该程序项的其他属性。输出的 TokenStream 将此属性宏应用的程序项替换为任意数量的程序项。
例如下面这个属性宏接受输入流并按原样返回实际上对属性并无操作。
#![crate_type proc-macro]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn return_as_is(_attr: TokenStream, item: TokenStream) - TokenStream {item
}下面示例在编译时输出
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn show_streams(attr: TokenStream, item: TokenStream) - TokenStream {println!(attr: \{}\, attr.to_string());println!(item: \{}\, item.to_string());item
}
使用属性宏
extern crate my_macro;
use my_macro::show_streams;
// 示例: 基础函数
#[show_streams]
fn invoke1() {}
// out: attr:
// out: item: fn invoke1() { }// 示例: 带输入参数的属性
#[show_streams(bar)]
fn invoke2() {}
// out: attr: bar
// out: item: fn invoke2() {}// 示例: 输入参数中有多个token的
#[show_streams(multiple tokens)]
fn invoke3() {}
// out: attr: multiple tokens
// out: item: fn invoke3() {}// 示例:
#[show_streams { delimiters }]
fn invoke4() {}
// out: attr: delimiters
// out: item: fn invoke4() {}