Rust-开发应用-enum转String常见解法

业务背景

今天在公司见到这么一段代码(已去除业务属性):

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.

这段代码功能上没有问题,但也有较明显的缺点:

  1. 代码不够简洁。
  2. 考虑扩展性,比如再加一种Job类型或将打印内容统一改成"Jobs",当前的写法都需要持续的增加修改代码。

为了解决这个问题,我们开始无情重构!

无情重构 - strum/strum_macros

我们可以通过strum来解决这个问题。

strum是一个增强rust中enumString的,由macrotrait组成的集合。当前版本为@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);
}
相关推荐
Rust研习社1 天前
组合真的优于继承吗?为什么 Rust 和 Go 都拥抱组合舍弃继承?
后端·rust·编程语言
红尘散仙3 天前
想写一个像样的终端 App?试试把 React 的开发体验搬进 Rust TUI
前端·rust
vivo互联网技术3 天前
从 Web 到桌面:基于 Tauri 2.0 + Vue 3 打造 vivo 线下门店「大头贴」拍照体验系统
前端·rust
Rust研习社3 天前
这 8 个 Rust 学习资源值得每个新手收藏起来
后端·rust·编程语言
星栈4 天前
10 分钟跑起第一个 Dioxus 应用:`dx` CLI、`rsx!` 和热更新好不好用
前端·rust·前端框架
望眼欲穿的程序猿4 天前
读取芯片内部温度传感器
嵌入式硬件·rust
望眼欲穿的程序猿4 天前
ADC 模拟电压采集
嵌入式硬件·rust
codexu_4612291874 天前
NoteGen 里一条记录如何变成 Markdown
前端·笔记·rust·tauri
Rust研习社4 天前
Rust 错误处理的黄金搭档:一个定义错误,一个传播错误
后端·rust·编程语言
techdashen4 天前
绕过系统 ICMP:用 rawsock、Npcap 和 WMI 找到默认网卡
开发语言·arm开发·rust