早些时候又被朋友怼了,产出太慢,今天继续接上回说到的自定义宏。
直接开码
话不多说咱直接开码,我们cargo new my_macro
创建一个包。然后修改main.rs
:
extern crate my_macro;
#[macro_use]
extern crate my_macro_derive;
use my_macro::MyMacro;
#[derive(MyMacro)]
struct Coder;
fn main() {
Corder::my_macro();
}
来来来,我们康康
上面的代码,我们看到了#[macro_use]
的语法,这是一个注解,可以使被注解的模块重的宏应用到当前的作用域。同样下面的我们又看到一个注解#[derive(MyMacro)]
,这个则是用来得到一个hello_macro函数的默认实现,也就是会生成对应的代码,然后再main里面尝试调用这个方法。
那么现在我们就要定义MyMacro这个trait(类似接口,我们后面再聊,嘤嘤嘤)了,我们需要新建一个库。可以使用以下命令cargo new my_macro --lib
,然后编辑生成的lib.rs文件
pub trait MyMacro {
fn my_macro();
}
怎么样,看着语法格式是不是就是一个接口;pub是rust中的关键字,表示这个trait是公开其他域可以访问的。
有了接口之后我们就要实现它对应的功能行为,那么我们希望这个接口打印一段话;我们可以这样修改:
extern crate my_macro;
use my_macro::MyMacro;
struct Coder;
impl MyMacro for Coder {
fn my_macro() {
println!("热点1:P照片是网红的基本素养");
println!("热点2:极限挑战");
println!("热点1:大家还是要坚持戴口罩");
}
}
fn main() {
Corder::my_macro();
}
这样我们可以任性的给任何想使用我们的my_macro的类型实现这个代码块啦,这样可以节约一部分工作。但是这样还不行,rust这个小伙子没有反射这个功能,所以他就没办法再运行的时候获取类型名,我们刚刚想偷懒的那部分就不行了,所以我们还需要一个再运行时生产代码的宏。我们习惯性的会在名字后面带上xxx_derive
来表明这是一个自定义的过程式宏包。cargo new my_macro_derive --lib
这两个lib息息相关,所以再同一个目录下创建是最好不过的事情,而且必须要同时加入依赖,发布的时候也得一起发布。然后我们需要将 my_macro_derive 声明为一个过程式宏的包。同时也需要 syn 和 quote 包中的功能,也需要将其加到依赖中。为 my_macro_derive 将下面的代码加入到 Cargo.toml 文件中,
注意是my_macro_derive包的配置哟。
[lib]
proc-macro = true
[dependencies]
syn = "*"
quote = "*"
修改完配置我们需要编辑my_macro_derive的lib.rs文件。
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
#[proc_macro_derive(MyMacro)]
pub fn my_macro_derive(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_derive_input(&s).unwrap();
let gen = impl_hello_macro(&ast);
gen.parse().unwrap()
}
重头戏来了,这段代码可算是有点看头了。刚刚我们新加的包是用来干啥的呢,proc_macro
可以将 Rust 代码转换为相应的字符串。syn
则将字符串中的Rust代码解析成为一个可以操作的数据结构。quote
则将 syn
解析的数据结构反过来传入到 Rust 代码中。知道了这些包的作用之后在看上面的代码一切就变得清晰和明了了。
函数接受一个输入转换为一个字符串类型的变量,然后这个字符串其实就是代表派生MyMacro
代码的字符串,然后syn
解析转换字符串成一个数据结构。这个结构可以让我们拿到所注解的名字如我们之前定义的Coder
名字等信息。
万事具备,只欠一个在注解类型上面实现我们刚刚MyMacro
的代码了。继续在 my_macro_derive
中编辑lib.rs
:
fn impl_my_macro(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
quote! {
impl MyMacro for #name {
fn my_macro() {
println!("你好热点送上了! 类型名: {}.", stringify!(#name));
}
}
}
}
大功告成。在最初的my_macro包中加入对应的依赖:
[dependencies]
hello_macro = { path = "../hello_macro" }
hello_macro_derive = { path = "../hello_macro/hello_macro_derive" }
然后执行文章开头的代码,cargo run
。就会打印出:你好热点送上了! 类型名:Corder.
码完跑路(偷懒)
所以接下来就是你们的时间了,好好捋捋上面说的东西吧。(逃
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。