4.4 KiB
4.4 KiB
Rust 宏编程(1)
rust 宏主要分为 派生宏、类函数宏、属性宏,可以非常方便的生成重复性代码。
终于可以能写会写代码的代码了罒ω罒。
环境准备
# dtolnay 大佬提供的 宏教学库
git clone https://github.com/dtolnay/proc-macro-workshop.git
## cargo expand 查看宏产生的全部代码
rustup toolchain install nightly-x86_64-apple-darwin
# 可先执行该指令 会提示你看着那个版本的nightly
cargo +nightly install cargo-expand
cargo test
- 目录结构
cd builder/
tree
.
├── Cargo.toml
├── src
│ └── lib.rs # 要编写宏的文件
└── tests
├── 01-parse.rs
├── 02-create-builder.rs
├── 03-call-setters.rs
├── 04-call-build.rs
├── 05-method-chaining.rs
├── 06-optional-field.rs
├── 07-repeated-field.rs
├── 08-unrecognized-attribute.rs
├── 08-unrecognized-attribute.stderr
├── 09-redefined-prelude-types.rs
└── progress.rs # 通过取消注释来确定运行那个test文件
- 编写宏代码
use proc_macro::TokenStream;
#[proc_macro_derive(Builder)]
pub fn derive(input: TokenStream) -> TokenStream {
let _ = input;
// 打印看看input到底是什么
eprintln!("{:#?}", input);
// 注释下行 并返回空的结果, 下行表示未实现该发放并进行panic
// unimplemented!()
TokenStream::new()
}
- 运行 test
# builder 目录下
cargo test
# 可以查看 test 运行成功无报错 并打印input 结构
TokenStream [
Ident {
ident: "pub",
span: #0 bytes(990..993),
},
Ident {
ident: "struct",
span: #0 bytes(994..1000),
},
Ident {
ident: "Command",
span: #0 bytes(1001..1008),
},
Group {
delimiter: Brace,
stream: TokenStream [
Ident {
ident: "executable",
span: #0 bytes(1015..1025),
},
....
....
...
Punct {
ch: ',',
spacing: Alone,
span: #0 bytes(1103..1104),
},
],
span: #0 bytes(1009..1106),
},
]
cargo expand
-
builder/src/lib.rs
use proc_macro::TokenStream; #[proc_macro_derive(Builder)] pub fn derive(input: TokenStream) -> TokenStream { eprintln!("{:#?}", input); // 这里直接把传入的结构返回回去 input } -
main.rs 根目录下
use derive_builder::Builder; #[derive(Builder)] pub struct Command { executable: String, args: Vec<String>, env: Vec<String>, current_dir: String, } fn main() {} -
查看扩展代码
# 进入根目录, 运行 cargo expand --bin workshop # 等同于 cargo expand # 输出#![feature(prelude_import)] #[prelude_import] use std::prelude::rust_2021::*; #[macro_use] extern crate std; use derive_builder::Builder; pub struct Command { executable: String, args: Vec<String>, env: Vec<String>, current_dir: String, } pub struct Command { executable: String, args: Vec<String>, env: Vec<String>, current_dir: String, } fn main() {}可以看到生成了一个 多的Command结构体
# 运行cargo test 可以看到报错 重复定义的Command ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ error[E0428]: the name `Command` is defined multiple times --> tests/01-parse.rs:27:1 | 27 | pub struct Command { | ^^^^^^^^^^^^^^^^^^ | | | `Command` redefined here | previous definition of the type `Command` here | = note: `Command` must be defined only once in the type namespace of this module ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
剩下的就是根据tests/目录下每个测试文件编写符合要求的宏代码。
祝各位顺利~
rust真TM难学,其他语言能看着demo直接上手写项目, rus已经看了一周多了,还没开始写实际代码。