mynote/rust/marco.md
2022-06-05 10:19:11 +08:00

199 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<String>,
env: Vec<String>,
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<String>,
env: Vec<String>,
current_dir: String,
}
pub struct Command {
executable: String,
args: Vec<String>,
env: Vec<String>,
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已经看了一周多了还没开始写实际代码。