业务背景
今天在公司见到这么一段代码(已去除业务属性):
rust
use std::fmt::Display;
enum Job {
Pre,
Post,
}
impl Display for Job {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Job::Pre => write!(f, "Pre Job"),
Job::Post => write!(f, "Post Job"),
}
}
}
pub fn main() {
println!("This is {}.", Job::Pre);
println!("This is {}.", Job::Post);
}
抽取出的代码功能很简单,其中定义了Pre(前置)和Post(后置)两种Job(工作)枚举,为了在日志打印信息,对其实现了std::fmt::Display特征。这里用main函数模拟打印日志的过程,打印结果为:
shell
This is Pre Job.
This is Post Job.
这段代码功能上没有问题,但也有较明显的缺点:
- 代码不够简洁。
- 考虑扩展性,比如再加一种Job类型或将打印内容统一改成"Jobs",当前的写法都需要持续的增加修改代码。
为了解决这个问题,我们开始无情重构!
无情重构 - strum/strum_macros
我们可以通过strum来解决这个问题。
strum是一个增强rust中enum和String的,由macro和trait组成的集合。当前版本为@0.27.1,其实现了如下macro:
| Macro | Description |
|---|---|
| EnumString | Converts strings to enum variants based on their name. |
| Display | Converts enum variants to strings |
| FromRepr | Convert from an integer to an enum. |
| AsRefStr | Implement AsRef<str> for MyEnum |
| IntoStaticStr | Implements From<MyEnum> for &'static str on an enum |
| EnumIter | Creates a new type that iterates of the variants of an enum. |
| EnumProperty | Add custom properties to enum variants. |
| EnumMessage | Add a verbose message to an enum variant. |
| EnumDiscriminants | Generate a new type with only the discriminant names. |
| EnumCount | Add a constant usize equal to the number of variants. |
| VariantArray | Adds an associated VARIANTS constant which is an array of all enum discriminants |
| VariantNames | Adds an associated VARIANTS constant which is an array of discriminant names |
| EnumTable | Experimental, creates a new type that stores an item of a specified type for each variant of the enum. |
在这里,我们需要使用Display这个macro。在项目中添加依赖:
shell
cargo add strum -F derive
cargo add strum_macros
通过如下代码实现原本功能:
rust
// use std::fmt::Display;
use strum::Display;
#[derive(Display)]
enum Job {
#[strum(serialize = "Pre Job")]
Pre,
#[strum(serialize = "Post Job")]
Post,
}
// impl Display for Job {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// match self {
// Job::Pre => write!(f, "Pre Job"),
// Job::Post => write!(f, "Post Job"),
// }
// }
// }
pub fn main() {
println!("This is {}.", Job::Pre);
println!("This is {}.", Job::Post);
}
无情重构 - derive_more
我们也可以通过derive_more来解决这个问题。
derive_more提供像Add, Not, From or Display这些trait的派生宏实现。当前版本为@2.0.1。
在项目中添加依赖:
shell
cargo add derive_more -F display
通过如下代码实现原本功能:
rust
// use std::fmt::Display;
use derive_more::Display;
#[derive(Display)]
#[display("{_variant} Job")]
enum Job {
Pre,
Post,
}
// impl Display for Job {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// match self {
// Job::Pre => write!(f, "Pre Job"),
// Job::Post => write!(f, "Post Job"),
// }
// }
// }
pub fn main() {
println!("This is {}.", Job::Pre);
println!("This is {}.", Job::Post);
}