欢迎光临
我们一直在努力

Rust 枚举还能这样玩?Strum 这个神级库了解一下

文章目录

  • Rust 枚举还能这样玩?Strum 这个神级库了解一下
    • 当前痛点
    • 场景一:枚举转字符串
    • 场景二:字符串转枚举
    • 场景三:遍历所有枚举值
    • 结语

Rust 枚举还能这样玩?Strum 这个神级库了解一下

很多 Rust 新手都知道可以通过派生宏来扩展枚举的能力,但其实 Rust 社区有一个非常流行的 Enum 增强库——Strum,通过它提供的宏能帮我们解决开发过程中很多的痛点。

当前痛点

在 Rust 中,枚举是一个非常强大的特性,很多语言里的枚举只是几个整数常量:

enum Color { RED, GREEN, BLUE };

而 Rust 的枚举不仅可以携带数据,还能用于状态机、错误处理、协议建模等场景。但还是不够方便,比如:

  • 枚举转字符串
  • 字符串转枚举
  • 遍历所有枚举值

这些功能如果自己手写,那么就需要手写很多样板代码,说实话挺麻烦的。而这时候就是强大的宏出场的时候了,接下来我们将通过一些使用场景来介绍 Strum 的用法。

首先,我们先在 Cargo.toml 添加上 strum 依赖:

[dependencies]
strum = { version = "0.28", features = ["derive"] }

场景一:枚举转字符串

有这么一个枚举:

enum Status {
Pending,
Running,
Finished,
}

如果需要将枚举转换成字符串,那么一般是通过实现 Display 特征来实现:

use std::fmt;

enum Status {
Pending,
Running,
Finished,
}

impl fmt::Display for Status {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Status::Pending => write!(f, "pending"),
Status::Running => write!(f, "running"),
Status::Finished => write!(f, "finished"),
}
}
}

fn main() {
println!("{}", Status::Pending);
println!("{}", Status::Running);
println!("{}", Status::Finished);
}

而使用 Strum 的 Display 派生宏来实现则非常简单:

use strum::Display;

#[derive(Display)]
enum Status {
#[strum(serialize = "pending")]
Pending,
#[strum(serialize = "running")]
Running,
#[strum(serialize = "finished")]
Finished,
}

fn main() {
println!("{}", Status::Pending);
println!("{}", Status::Running);
println!("{}", Status::Finished);
}

本质上就是 Strum 自动帮我们实现了 Display 特征,说白了就是自动生成这些样板代码。

场景二:字符串转枚举

依旧是这个枚举:

enum Status {
Pending,
Running,
Finished,
}

假设我们现在需要将 pending 字符串转换为对应的枚举,那么可以通过实现 FromStr 特征来实现:

use std::str::FromStr;

#[derive(Debug)]
enum Status {
Pending,
Running,
Finished,
}

impl FromStr for Status {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"pending" => Ok(Status::Pending),
"running" => Ok(Status::Running),
"finished" => Ok(Status::Finished),
_ => Err("invalid".into()),
}
}
}

fn main() {
println!("{:?}", "pending".parse::<Status>());
println!("{:?}", "running".parse::<Status>());
println!("{:?}", "finished".parse::<Status>());
}

这里可以使用 Strum 的 EnumString 派生宏来进行简化:

use strum::EnumString;

#[derive(Debug, EnumString)]
enum Status {
#[strum(serialize = "pending")]
Pending,
#[strum(serialize = "running")]
Running,
#[strum(serialize = "finished")]
Finished,
}

fn main() {
println!("{:?}", "pending".parse::<Status>());
println!("{:?}", "running".parse::<Status>());
println!("{:?}", "finished".parse::<Status>());
}

场景三:遍历所有枚举值

一般情况下,如果我们想遍历枚举值,得专门构建出一个枚举数组来使用,比如:

fn all_status() -> Vec<Status> {
vec![
Status::Running,
Status::Finished,
Status::Cancelled,
]
}

其实这样挺麻烦的,万一有新增或减少枚举值,还得同步进行修改。而使用 Strum 的 EnumIter 派生宏就能解决这个问题:

use strum::{EnumIter, IntoEnumIterator};

#[derive(Debug, EnumIter)]
enum Status {
Pending,
Running,
Finished,
}

fn main() {
for status in Status::iter() {
println!("{:?}", status);
}
}

结语

在这篇文章里,虽然我只讲了三种用法,但是其实已经覆盖了 90% 的 Strum 使用场景了,感兴趣的读者可以自行阅读 Strum 官方文档继续探索。

不过,比起 Strum 本身,我更希望大家关注的是如何善用宏来消除重复代码。

很多 Rust 新手在学习时会下意识地避开宏,确实它很难,但你会发现宏不单是 Rust 的高级技巧,还是写出地道 Rust 代码的关键手段之一。Rust 生态中最优秀的库几乎都在使用宏写出更简洁、更安全、更易维护的代码。

赞(0)
未经允许不得转载:171主机测评 » Rust 枚举还能这样玩?Strum 这个神级库了解一下
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址