接上文。
宏的分类与实现举例
- 声明宏(Declarative Macro)
 - 过程宏 (Procedural Macro)
    
- 自定义derive宏 (Custom derive)
 - 属性宏 (Attribute-like macros)
 - 函数宏 (Function-like macros)
 
 
声明宏
像vec!、concat!这类常用的宏就是用声明宏实现的,可以方便coding,减少代码量。
这里我们举个例子,rust实现里面是不允许出现let a=b=c="1+2";这类代码的。
我们考虑实现一个宏assign,可以assign!("1+2", a, b, c)来达到上述目的。
考虑到刚刚入门,可以从简单的开始实现。
assign!(a, "1+2")assign!("1+2", a)assign!("1+2", a, b)assign!("1+2", a, b, c, ...)
#[macro_export]
macro_rules! assign {
    // 实现 assign!("1+2", a) 类型
    ( $val:expr, $it:ident) => {
        let $it = $val;
    };
    // 实现 assign!("1+2", a, b) 类型
    ( $val:expr, $it:ident, $it2:ident) => {
        let $it = $val;
        let $it2 = $val;
    };
    // 实现 assign!("1+2", a, b, c, d, ...) 任意个参数类型
    ( $val:expr, $( $it:ident ),*) => {
        $(
            let $it = $val;
        )*
    };
}
fn main() {
    assign!("1+2", a);
    println!("a: {}", a);
    assign!(1 + 2, a);
    println!("a: {}", a);
    assign!(1 + 2, a, b);
    println!("a = b = {} = {}", a, b);
    assign!(5 + 2, a, b, c);
    println!("a = b = c = {} = {} = {}", a, b, c);
}
自定义derive宏
我们在打印的时候,常常会被提示没有实现Debug trait,建议添加#[derive(Debug)]。
也就是说,derive宏可以帮我们实现指定的trait。具体如何实现呢?
举个例子,我们将要实现3个trait:
s06_macro: 任意名称,定义traitHelloMacros06_macro_derive: 前一个crate名称 +_derive,实现traitHelloMacroasdssafsfs: 任意名称,引用traitHelloMacro并使用derive宏实现它
s06_macro
// src/lib.rs
pub trait HelloMacro {
    fn hello_macro();
}
s06_macro_derive
# Cargo.toml
[lib]
proc-macro = true
[dependencies]
# 用于将TokenStream转化为Rust语法树
syn = "1.0"
# 用于将内容转化为TokenStream
quote = "1.0"
se proc_macro::TokenStream;
use quote::quote;
use syn::{self};
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // Construct a representation of Rust code as a syntax tree
    // that we can manipulate
    let ast = syn::parse(input).unwrap();
    // Build the trait implementation
    impl_hello_macro(&ast)
}
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let gen = quote! {
        impl HelloMacro for #name {
            fn hello_macro() {
                println!("Hello, Macro! My name is {}!", stringify!(#name));
            }
        }
    };
    gen.into()
}
示例
# Cargo.toml
[dependencies]
s06_macro = { path = "../s06_macro" }
s06_macro_derive = { path = "../s06_macro_derive" }
// src/main.rs
#[derive(s06_macro_derive::HelloMacro)]
struct Test;
fn main() {
    Test::hello_macro();
    // 将会打印`Hello, Macro! My name is Test!`
}
属性宏
我看的教程举例用的是路由:
#[route(GET, "/")]
fn index() {
找到了一个最简单的例子,它会把宏的参数和加宏的函数在编译的时候全打印出来,加宏的函数仍然是原来的功能。
如果你要想更复杂的实现,需要参照前面一样,将TokenStream转为语法树,再来一顿猛如虎的操作,然后转化为TokenStream返回。
// src/lib.rs
#[proc_macro_attribute]
pub fn show_streams(attr: TokenStream, item: TokenStream) -> TokenStream {
    // 下面的打印发生在编译时期
    println!("attr: \"{}\"", attr.to_string());
    println!("item: \"{}\"", item.to_string());
    item
}
// Attribute macros - #[CustomAttribute]
// Example: Basic function
#[show_streams]
fn invoke1() {}
// out: attr: ""
// out: item: "fn invoke1() { }"
// Example: Attribute with input
#[show_streams(bar)]
fn invoke2() {}
// out: attr: "bar"
// out: item: "fn invoke2() {}"
// Example: Multiple tokens in the input
#[show_streams(multiple => tokens)]
fn invoke3() {}
// out: attr: "multiple => tokens"
// out: item: "fn invoke3() {}"
// Example:
#[show_streams { delimiters }]
fn invoke4() {}
// out: attr: "delimiters"
// out: item: "fn invoke4() {}"
// src/main.rs
fn main() {
    Test::hello_macro();
    invoke1();
    invoke2();
    invoke3();
    invoke4();
}
函数宏
我看的教程举例用的是sql,它返回的是一个值:
let sql = sql!(SELECT * FROM posts WHERE id=1);
实际上,脑洞还可以放开一点
make_answer!(xxx); // 创建一个answer函数
let answer0 = answer();
print_hello!(); // 打印hello输出
// src/lib.rs
#[proc_macro]
pub fn make_answer(_item: TokenStream) -> TokenStream {
    "fn answer() -> u32 { 42 }".parse().unwrap()
}
#[proc_macro]
pub fn print_hello(_item: TokenStream) -> TokenStream {
    "println!(\"hello\")".parse().unwrap()
}
常见的宏应用举例
features
- 定义features
 
# Cargo.toml
[dependencies]
gif = { version = "0.11.1", optional = true }
[features]
default = ["bmp"]
bmp = []
png = []
ico = ["bmp", "png"]
webp = []
gif = ["dep:gif"] # gif feature开启才会引入依赖gif
- 实现features
 
#[cfg(feature = "png")]
pub mod PNG;
// ...
#[cfg(feature = "webp")]
pub struct Webp{}
- 使用
 
# Cargo.toml
[dependencies]
s06_macro = {features=["png", "gif"]}
条件编译
features实际上就是运用了条件编译。
具体来说,就是对cfg宏的使用。
- cfg属性:在属性位置中使用
#[cfg(…)] - cfg!宏:在布尔表达式中使用
cfg!(…) 
// 参考 https://blog.csdn.net/lcloveyou/article/details/105004181
// 仅当目标系统为Linux 的时候才会编译
#[cfg(target_os = "linux")]
fn are_you_on_linux() {
	println!("linux!")
}
// 仅当目标系统不是Linux 时才会编译
#[cfg(not(target_os = "linux"))]
fn are_you_on_linux() {
	println!("not linux!")
}
fn main() {
	are_you_on_linux();
	println!("Are you sure?");
	if cfg!(target_os = "linux") {
		println!("Yes. It's linux!");
	} else {
		println!("Yes. It's not linux!");
	}
}