Rust的宏有两种类型:声明式和程序式。声明式的宏很像C语言的宏,而过程式的宏很像Java有反射加持的AOP。
而程序式根据作用的位置(顶在谁头上)不同,分成自定义derive
,函数
,属性
三种。
在当前目录下创建三个工程:
crate new use_demo
crate new --lib hello_macro
crate new --lib hello_macro_derive
hello_macro包
lib.rs:
pub trait HelloMacro {`在这里插入代码片`
fn hello_macro();
}
hello_macro_derive包
lib.rs:
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
let ast = syn::parse(input).unwrap();
// Build the trait implementation
impl_hello_macro(&ast)
}
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!("Hello, Macro! My name is {}!", stringify!(#name));
}
}
};
gen.into()
}
Cargo.toml
[lib]
proc-macro = true
[dependencies]
syn = "1.0"
quote = "1.0"
use_macro包
main.rs
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;
#[derive(HelloMacro)]
struct Pancakes;
fn main() {
Pancakes::hello_macro();
}
Cargo.toml
[dependencies]
hello_macro = { path = "../hello_macro" }
hello_macro_derive = { path = "../hello_macro/hello_macro_derive" }
运行结果
$ cargo run
Compiling proc-macro2 v0.4.30
Compiling unicode-xid v0.1.0
Compiling hello_macro v0.1.0 (G:\workspace_rh\bbc\study2\hello_macro)
Compiling quote v0.6.13
Compiling syn v0.14.9
Compiling hello_macro_derive v0.1.0 (G:\workspace_rh\bbc\study2\hello_macro\hello_macro_derive)
Compiling use_macro v0.1.0 (G:\workspace_rh\bbc\study2\use_macro)
Finished dev [unoptimized + debuginfo] target(s) in 8.67s
Runningtarget\debug\use_macro.exe
Hello, Macro! My name is Pancakes
总结
- 世界上本没有宏,有了
proc-macro = true
它(Trait)就变成了宏。原本Trait是一个安安静静的Trait,是#[proc_macro_derive(HelloMacro)]
把它和一个专门用来做宏动作(宏定义)的lib.rs绑到了一起。 proc-macro = true
必须放到宏定义所在的package。因为:the#[proc_macro_derive]
attribute is only usable with crates of theproc-macro
crate type- 定义宏的lib.rs,只能定义宏,而不能有其它的item。至少目前是这样(
proc-macro
crate types currently cannot export any items other than fun ctions tagged with#[proc_macro]
,#[proc_macro_derive]
, or#[proc_macro_att ribute]
) - 这就是为什么,一些开源比如Diesel,把别人(第三方)的Trait,通过自己增加宏定义的方式使用它。