Rust 中的宏主要分为两大类:声明宏和过程宏。
什么是属性宏
属性宏(Attribute Macro)
是 Rust 的一种 过程宏(Procedural Macro)
,允许你在编译时修改或生成代码。它的特点是通过 #[...]
语法附加到代码项(如函数、结构体、模块等)上,接收属性参数和代码项本身作为输入,然后生成新的代码。
属性宏可以分为两种类型:
• 内建属性宏(Built-in Attribute Macros):由 Rust 编译器提供,用于控制编译器的行为或提供特定的功能。
• 自定义属性宏(Custom Attribute Macros):由用户或第三方库定义,用于扩展 Rust 的功能或实现特定的逻辑。
常见的场景属性宏
以下是一些常见的内建属性宏及其应用场景:
(1)#[cfg]
• 作用:用于条件编译,根据特定的配置条件决定是否编译某段代码。
• 示例:
rust
#[cfg(target_os = "linux")]
fn foo() {
println!("Running on Linux");
}
#[cfg(target_os = "windows")]
fn foo() {
println!("Running on Windows");
}
fn main() {
foo();
}
• 场景:跨平台开发时,根据不同的操作系统或目标环境编译不同的代码。
(2)#[derive]
• 作用:为类型自动派生实现某些标准 trait(如Debug
、Clone
、PartialEq
等)。
• 示例:
rust
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
• 场景:快速为类型实现标准 trait,减少样板代码。
(3)#[test]
• 作用:标记一个函数为测试函数,该函数将在运行测试时被调用。
• 示例:
rust
#[test]
fn test_addition() {
assert_eq!(2 + 2, 4);
}
• 场景:单元测试和集成测试。
(4)#[allow]
和#[warn]
• 作用:用于控制编译器的警告和错误提示。
• 示例:
rust
#[allow(dead_code)]
fn unused_function() {
println!("This function is not used");
}
• 场景:在开发过程中忽略某些特定的警告,或者强制某些警告提升为错误。
(5)#[repr]
• 作用:指定类型在内存中的表示形式。
• 示例:
rust
#[repr(u8)]
enum Color {
Red = 0,
Green = 1,
Blue = 2,
}
• 场景:与外部系统交互时,确保类型在内存中的布局与外部系统一致。
(6)#[no_mangle]
• 作用:禁止编译器对函数名或变量名进行名称修饰(mangling),保留其原始名称。
• 示例:
rust
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}
• 场景:与 C 语言代码交互时,确保函数名与 C 语言中的名称一致。
属性宏与派生宏的关系
(1)定义
• 派生宏(Derive Macros):是一种特殊的属性宏,用于为类型(如结构体或枚举)自动派生实现某些 trait。它们的语法是#[derive(TraitName)]
。
• 属性宏(Attribute Macros):更广泛的概念,包括派生宏和其他类型的属性宏。属性宏的语法是#[attribute_name]
。
(2)关系
• 派生宏是属性宏的一个子集:派生宏是属性宏的一种特殊形式,专门用于实现 trait。
• 属性宏的功能更广泛:除了派生宏的功能外,属性宏还可以用于控制编译器行为、生成代码、修改代码语义等。
(3)区别
• 用途:
派生宏:主要用于为类型自动实现标准 trait,减少样板代码。
属性宏:用途更广泛,包括条件编译、测试、控制编译器行为等。
• 语法:
派生宏:#[derive(Debug, Clone, PartialEq)]
属性宏:#[cfg(target_os = "linux")]
、#[test]
、#[allow(dead_code)]
等。
常用的内置派生宏
Rust 中内置的派生宏主要有以下几个:
1. Debug
- 作用 :为结构体或枚举体实现 Debug trait,允许使用调试格式打印,通常使用宏
{:?}
或{:#?}
来格式化输出。这在调试时非常有用,可以方便地查看变量的值。 - 示例 :
rust
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 1, y: 2 };
println!("p is {:?}", p);
println!("p is {:#?}", p);
}
2. Clone
- 作用 :为结构体或枚举体实现 Clone trait,允许通过调用
clone()
方法来创建一个类型副本。 - 示例 :
rust
#[derive(Clone)]
struct Person {
name: String,
age: u8,
}
fn main() {
let person1 = Person {
name: String::from("Alice"),
age: 30,
};
let person2 = person1.clone();
println!("person1: name = {}, age = {}", person1.name, person1.age);
println!("person2: name = {}, age = {}", person2.name, person2.age);
}
3. Copy
- 作用 :为标量类型组合(如元组或包含全部为 Copy 类型的结构体)实现 Copy trait,使得值在赋值或传递时会进行浅复制,而原始值仍然可用。
- 示例 :
rust
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1;
println!("p1: x = {}, y = {}", p1.x, p1.y);
println!("p2: x = {}, y = {}", p2.x, p2.y);
}
4. PartialEq
- 作用 :为结构体或枚举体实现 PartialEq trait,允许进行相等比较,使用
==
或!=
操作符。 - 示例 :
rust
#[derive(PartialEq)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 2 };
let p3 = Point { x: 3, y: 4 };
println!("p1 == p2: {}", p1 == p2);
println!("p1 == p3: {}", p1 == p3);
}
5. Eq
- 作用 :在实现 PartialEq 的基础上,进一步实现 Eq trait,表明该类型满足相等关系的等价性(即满足自反性、对称性和传递性)。只有当所有字段都实现了 Eq 时,才可以为该类型派生 Eq。
- 示例 :
rust
#[derive(PartialEq, Eq)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 2 };
let p3 = Point { x: 3, y: 4 };
println!("p1 == p2: {}", p1 == p2);
println!("p1 == p3: {}", p1 == p3);
}
6. PartialOrd
- 作用 :为结构体或枚举体实现 PartialOrd trait,允许进行大小比较,使用
<
、<=
、>
和>=
操作符。 - 示例 :
rust
#[derive(PartialOrd, PartialEq)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 3 };
println!("p1 < p2: {}", p1 < p2);
println!("p1 > p2: {}", p1 > p2);
}
7. Ord
- 作用 :在实现 PartialOrd 和 Eq 的基础上,进一步实现 Ord trait,表明该类型满足全序关系,即可以进行确定的大小排序。这允许该类型用于需要有序集合的场景,如 BTreeSet 和 BTreeMap。
- 示例 :
rust
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut points = vec![
Point { x: 2, y: 3 },
Point { x: 1, y: 2 },
Point { x: 3, y: 4 },
];
points.sort();
for p in points {
println!("({}, {})", p.x, p.y);
}
}
8. Hash
- 作用 :为结构体或枚举体实现 Hash trait,允许该类型用于哈希集合(如 HashSet 和 HashMap)的键。
- 示例 :
rust
use std::collections::HashSet;
#[derive(Hash, Eq, PartialEq, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut set = HashSet::new();
set.insert(Point { x: 1, y: 2 });
set.insert(Point { x: 3, y: 4 });
println!("set contains Point(1,2): {}", set.contains(&Point { x: 1, y: 2 }));
}