Codes in lesson21
常用宏
1 2 3 4 5 6
| println!("Hello, world!"); println! {"Hello, world!"}; println!["Hello, world!"]; let v = vec![1, 2, 3, 4, 5]; assert_eq!(1, 10); panic!("Something went wrong!");
|
声明宏
声明宏(Declarative Macros),也称为 macro_rules!,是最常⻅的宏类型。它们允许通过模式匹配来⽣成代码
1 2 3 4 5 6 7 8 9 10 11 12
| #[macro_export] macro_rules! say_hello { () => { println!("Hello, world!"); }; } fn main() { say_hello!(); say_hello! {}; say_hello![]; }
|
实现变长参数
宏比起函数,可以实现变长参数
变长参数是通过一种叫“重复模式”的机制实现的,它由三部分组成:
- 捕获组
$( ... ):这表示一组可以重复的内容
- 分隔符 ,:用来分隔多个参数(可以是其他符号)
- 重复指示器 * 或 + -:表示这组内容可以重复多少次
1 2 3 4 5 6 7 8 9 10 11
| macro_rules! my_macro { ($($arg:expr),*) => { $( println!("Got argument: {}", $arg); )* }; }
|
$($arg:expr):捕获一个表达式并命名为 $arg$arg 是变量名(可以自定义):expr 表示匹配的表达式类型
- ,:每个参数之间的分隔符
- *:表示这个模式可以匹配0次或多次+:表示匹配1次或多次(至少一个参数)
处理不同的参数类型
表达式(expressions)
标识符(identifiers)
类型(types)
任意令牌(tokens)
多种分隔符
自定义分隔符,且支持不同分隔符的多个模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| macro_rules! print_multiple { ($($arg:expr) *) => { $( println!("Arg: {}", $arg); )* }; ($($arg:expr), *) => { $( println!("Arg: {}", $arg); )* }; ($($arg:expr); *) => { $( println!("Arg: {}", $arg); )* }; } print_multiple![1 2 3]; print_multiple![4, 5, 6]; print_multiple![7; 8; 9];
|
过程宏
过程宏(Procedural Macros)允许使⽤函数⽣成代码,分为三种类型:
- 派生宏
- 属性宏(Attribute-like macro)
- 函数宏(Function-like macro)
过程宏在编译时执行,需要:
- 作为独立的 crate 编写
- 在 toml 文件中特殊标记(proc-macro = true)
- 接收 TokenStream 作为输入
- 输出新的 TokenStream
过程宏配置
Cargo.toml 文件内容:
1 2 3 4 5 6
| [lib] proc-macro = true [dependencies] syn = "2.0" quote = "1.0" proc-macro2 = "1.0"
|
项目结构:
1 2 3 4 5 6 7
| my_macros/ ├── Cargo.toml └── src/ ├── main.rs # 主入口 ├── derive_macros.rs # 派生宏 ├── attr_macros.rs # 属性宏 └── fn_macros.rs # 函数式宏
|
派生宏
派生宏 #[derive(MyMacro)] 用于自动为结构体和枚举实现 trait
#[derive(Debug)]
自动实现 std::fmt::Debug trait,用于调试输出
1 2 3
| #[derive(Debug)] struct Point { x: i32, y: i32 } println!("{:?}", Point { x: 1, y: 2 });
|
#[derive(Clone)]
自动实现 Clone trait,允许值复制
1 2 3 4 5 6 7 8
| #[derive(Clone)] struct Data { content: String, } fn main() { let data = Data { content: "Hello".to_string() }; let cloned_data = data.clone(); }
|
#[derive(Copy)]
将类型标记为复制语义(需同时实现 Clone)
1 2 3 4 5 6 7 8 9
| #[derive(Copy, Clone)] struct Position { x: u8, y: u8, } fn main() { let pos1 = Position { x: 5, y: 10 }; let pos2 = pos1; }
|
#[derive(PartialEq, Eq)]
实现相等比较操作符 == 和 !=
1 2 3 4 5 6 7
| #[derive(PartialEq, Eq)] struct UserId(u64); fn main() { let id1 = UserId(1001); let id2 = UserId(1002); assert!(id1 != id2); }
|
#[derive(PartialOrd, Ord)]
实现比较操作符 <, >, <=, >=
1 2 3 4 5 6 7
| #[derive(PartialOrd, Ord, PartialEq, Eq)] struct Priority(u8); fn main() { let p_low = Priority(1); let p_high = Priority(10); assert!(p_low < p_high); }
|
#[derive(Default)]
自动生成默认值实现
1 2 3 4 5 6 7 8
| #[derive(Default)] struct Settings { timeout: u32, retry_count: u8, } fn main() { let settings = Settings::default(); }
|
属性宏
属性宏 #[my_macro] 用于自定义属性处理
#[cfg]
用于条件编译
1 2 3 4 5 6 7 8 9 10 11
| #[cfg(target_os = "windows")] fn get_system_info() -> String { "Windows system".to_string()
#[cfg(target_os = "linux")] fn get_system_info() -> String { "Linux system".to_string() } fn main() { println!("Running on: {}", get_system_info()); }
|
#[test]
用于单元测试
1 2 3 4 5 6 7 8 9 10 11 12
| #[cfg(test)] mod tests { #[test] fn test_addition() { assert_eq!(2 + 2, 4); } #[test] #[should_panic(expected = "out of bounds")] fn test_panic() { vec![1, 2, 3][10]; } }
|
#[allow] / #[warn] / #[deny]
用于代码检查控制
1 2 3 4 5 6 7 8 9 10 11 12
| #[allow(unused_variables)] fn unused_example() { let x = 5; }
#[warn(missing_docs)] pub struct NoDocumentation;
#[deny(deprecated)] fn no_deprecated() { }
|
#[repr]
用于内存表示控制
1 2 3 4 5 6 7 8 9 10 11
| #[repr(C)] struct CCompatible { x: u32, y: u32, }
#[repr(packed)] struct PackedData { flag: u8, value: u32, }
|
函数宏
函数宏 my_macro!() 是类似函数调用的宏
用于格式化输出
1 2 3 4 5 6
| let name = "Alice"; let age = 30;
println!("Hello, {}! You are {} years old.", name, age);
let greeting = format!("Hello, {}! Age: {}", name, age);
|
vec!
用于向量创建
1 2 3 4 5 6
| let empty: Vec<i32> = vec![];
let numbers = vec![1, 2, 3, 4, 5];
let zeros = vec![0; 10];
|
assert! / assert_eq! / assert_ne!
用于测试断言
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| fn calculate(a: i32, b: i32) -> i32 { a + b } fn main() { let result = calculate(2, 3); assert!(result == 5); assert_eq!(result, 5); assert_ne!(result, 10); assert!( result > 0, "Result must be positive, got {}", result ); }
|
panic!
用于触发恐慌
1 2 3 4 5 6
| fn divide(numerator: f64, denominator: f64) -> f64 { if denominator == 0.0 { panic!("Division by zero!"); } numerator / denominator }
|
file!, line!, column!
用于展示代码位置信息
1 2 3 4 5 6 7 8 9 10 11 12
| fn log(message: &str) { println!( "[{}:{}:{}] {}", file!(), line!(), column!(), message ); } fn main() { log("Starting program"); }
|
concat!
用于连接文字
1 2 3 4 5 6 7 8 9
| const VERSION_STRING: &str = concat!( "App v", env!("CARGO_PKG_VERSION"), " built at ", env!("BUILD_TIMESTAMP") ); fn main() { println!("{}", VERSION_STRING); }
|
课后习题
通过 macro_rules! 实现对应的 macro,并通过测试 case
1 2 3
| assert_eq!(repeat!("x",3) ,"xxx"); assert_eq!(sum!(1,2,3,4,5), 15); assert_eq!(max_value!(1,8,9), 9);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #[macro_export] macro_rules! repeat { ($item:expr, 0) => { "" }; ($item:expr, $n:expr) => {{ let mut result = String::with_capacity($item.len() * $n); for _ in 0..$n { result.push_str($item); } result }}; }
macro_rules! sum { ($x:expr) => { $x }; ($x:expr, $($y:expr),+) => {{ $x + sum!($($y),+) }}; }
macro_rules! max_value { ($x:expr, $y:expr) => { if $x > $y { $x } else { $y } }; ($x:expr, $($y:expr),+) => { max_value!($x, max_value!($($y),+)) }; }
|