# Rust 宏编程(1) rust 宏主要分为 派生宏、类函数宏、属性宏,可以非常方便的生成重复性代码。 终于可以能写会写代码的代码了罒ω罒。 ### 环境准备 ```bash # 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 - 目录结构 ```bash 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文件 ``` - 编写宏代码 ```rust use proc_macro::TokenStream; #[proc_macro_derive(Builder)] pub fn derive(input: TokenStream) -> TokenStream { let _ = input; // 打印看看input到底是什么 eprintln!("{:#?}", input); // 注释下行 并返回空的结果, 下行表示未实现该发放并进行panic // unimplemented!() TokenStream::new() } ``` - 运行 test ```bash # 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 ```rust use proc_macro::TokenStream; #[proc_macro_derive(Builder)] pub fn derive(input: TokenStream) -> TokenStream { eprintln!("{:#?}", input); // 这里直接把传入的结构返回回去 input } ``` - main.rs 根目录下 ```rust use derive_builder::Builder; #[derive(Builder)] pub struct Command { executable: String, args: Vec, env: Vec, current_dir: String, } fn main() {} ``` - 查看扩展代码 ```bash # 进入根目录, 运行 cargo expand --bin workshop # 等同于 cargo expand # 输出 ``` ```rust #![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, env: Vec, current_dir: String, } pub struct Command { executable: String, args: Vec, env: Vec, current_dir: String, } fn main() {} ``` 可以看到生成了一个 多的Command结构体 ```bash # 运行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已经看了一周多了,还没开始写实际代码。