22、Derive macros(派生宏)
Derive macros(派生宏)
上一节,我们为Ticket结构体实现了PartialEq这个trait,使其可以使用比较运算符(==、!=),但是,如果我们想要为大量的结构体实现这个比较基础的trait,那么我们可能要手动写大量的代码,可能重复性很高,导致代码又臭又长。
为了解决这个问题,Rust引入了Derive macros(派生宏)
解构语法
除此之外,这种方式也很难维护。如果我们为自定义的结构体添加或修改了某些字段,我们必须专门去修改PartialEq的实现,导致很难维护。
我们可以通过把结构体解构到它的字段(新建一个Ticket实例实现比较逻辑)来减轻这个风险:
impl PartialEq for Ticket {
fn eq(&self, other: &Self) -> bool {
let Ticket {
title,
description,
status,
} = self;
// [...]
}
}
像这样,如果我们修改了Ticket的字段,那么这里的赋值(模式匹配)就会发生报错,提醒我们去修改这里的代码。
我们还可以重命名结构体字段,避免变量遮蔽:
impl PartialEq for Ticket {
fn eq(&self, other: &Self) -> bool {
let Ticket {
title,
description,
status,
} = self;
let Ticket {
title: other_title,
description: other_description,
status: other_status,
} = other;
// [...]
}
}
在我们的工具包中,解构是一种有用的模式,但是还有另外一种更好的方式,就是派生宏
宏
在之前的学习中,我们遇到了一些宏:
- 在测试实例中的
assert_eq!和assert! - 用于在控制台打印内容的
println!
Rust的宏就是代码生成器!
它们根据我们提供的输入生成新的Rust代码,生成的代码会与程序的其他部分一起编译。一些宏内置在Rust的标准库中,我们也可以编写自己的宏,但是现在先不打算学,之后再学。(The little book of Rust macros和Proc macro workshop)
检查
有些IDE允许我们展开宏来检查生成的代码,如果不行的话,可以使用cargo-expand
派生宏
派生宏是一种特殊的Rust宏,它被指定为结构体顶部的属性:
#[derive(PartialEq)]
struct Ticket {
title: String,
description: String,
status: String
}
派生宏可以用来为自定义的类型实现一些常见的traits。上面的代码就自动为Ticket实现了Partialtrait。
展开宏之后就可以发现代码与我们写的代码基本相同

执行了cargo expand后,可以发现,里面确实生成了和我们之前写的几乎一样的代码
编译器在可能的情况下尽可能推动我们派生trait
练习
// TODO: A (derivable) trait implementation is missing for this exercise to compile successfully.
// Fix it!
//
// # `Debug` primer
//
// `Debug` returns a representation of a Rust type that's suitable for debugging (hence the name).
// `assert_eq!` requires `Ticket` to implement `Debug` because, when the assertion fails, it tries to
// print both sides of the comparison to the terminal.
// If the compared type doesn't implement `Debug`, it doesn't know how to represent them!
#[derive(/* TODO */PartialEq)]
pub struct Ticket {
title: String,
description: String,
status: String,
}
impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket {
Ticket {
title,
description,
status,
}
}
}
这里是让我们为Ticket实现Debug这个派生宏,以实现Debug trait
我们直接加上Debug即可!
#[derive( Debug,PartialEq)]
pub struct Ticket {
title: String,
description: String,
status: String,
}
也是顺利通过了!
这一节的学习就先到这里了…
