
宏的作用就是在编译期间对原代码进行扩展,实现目标功能。简单的说宏就是生成代码的代码。
官方解释:
You can sort of think of procedural macros as functions from an AST(抽象语法树) to another AST.
(你可以简单认为,过程宏是一个将原有AST语法树转换为另外一个AST语法树的函数)
个人理解,Rust 宏相比C++中的宏定义, 它提供了一种可用让开发人员更容易介入代码编译过程的入口
Rust 过程宏定义分三种
和macro_rules! 功能类似,但更为强大。
2. #[proc_macro_derive(Name)] 派生宏
用于结构体(struct)、枚举(enum)、联合(union)类型,可为其实现函数或特征(Trait)
3. #[proc_macro_attribute] 属性宏
用于属性宏, 用在结构体、字段、函数等地方,为其指定属性等功能
其中 Derive Macro 派生宏和 Attribute Marco 宏的区别是:派生宏生成代码,然后将其添加到同一模块中,而属性宏生成代码来替换它们所应用的项目。 派生宏用于扩展, 属性宏用于替换。后面在例子中说明。
custom
Cargo.toml
[package]
name = "custom"
version = "0.1.0"
[dependencies]
custom-derive={path="custom-derive"}custom-derive
Cargo.toml
[package]
name="custom-derive"
version="0.1.0"
[lib]
proc-macro = true # 使用过程宏
[dependencies]
# quote = "1.0.9" # 目前没用到,先注释了
# proc-macro2 = "1.0.27"
# syn = {version="1.0.72", features=["full"]}
3. lib.rs
use proc_macro::TokenStream;
extern crate proc_macro;
// 函数式宏
#[proc_macro]
pub fn make_hello(item: TokenStream) -> TokenStream {
let name = item.to_string();
let hell = "Hello ".to_string() + name.as_ref();
let fn_name =
"fn hello_".to_string() + name.as_ref() + "(){ println!(\"" + hell.as_ref() + "\"); }";
fn_name.parse().unwrap()
}
// 属性宏 (两个参数)
#[proc_macro_attribute]
pub fn log_attr(attr:TokenStream, item:TokenStream)->TokenStream{
println!("Attr:{}", attr.to_string());
println!("Item:{}", item.to_string());
item
}
// 派生宏
#[proc_macro_derive(Hello)]
pub fn hello_derive(input: TokenStream)-> TokenStream {
println!("{:?}", input);
TokenStream::new()
// 如果直接返回input,编译会报重复定义,说明派生宏用于扩展定义
// input
}TokenStream 相当编译过程中的语法树的流。
4. main.rs
extern crate custom_derive;
use custom_derive::log_attr;
use custom_derive::make_hello;
use custom_derive::Hello;
make_hello!(world);
make_hello!(张三);
#[log_attr(struct, "world")]
struct Hello{
pub name: String,
}
#[log_attr(func, "test")]
fn invoked(){}
#[derive(Hello)]
struct World;
fn main() {
// 使用make_hello生成
hello_world();
hello_张三();
}make_hello 使用#[proc_macro]
定义自动生成一个传入参数函数。
Hello world
Hello 张三
log_attr 使用#[proc_macro_attribute]
编译期间会打印结构类型和参数,后面可用修改替换原属性定义。
Attr:struct, "world"
Item:struct Hello { pub name : String, }
Attr:func, "test"
Item:fn invoked() { }
#[derive(Hello)] 使用#[proc_macro_derive(Hello)]·
会打印当前TokenStream 结点流,后面可以合 syn 与 quto 库结合,扩展定义。
TokenStream [Ident { ident: "struct", span: #0 bytes(286..292) }, Ident { ident: "World", span: #0 bytes(293..298) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(298..299) }]
后续: syn 与 quto 使用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。