当前位置:首页 > 科技  > 软件

一起聊聊在Rust中使用枚举表示状态

来源: 责编: 时间:2024-04-07 17:05:54 270观看
导读许多具有系统编程背景的Rust初学者倾向于使用bool(甚至u8—8位无符号整数类型)来表示“状态”。例如,如何使用bool来指示用户是否处于活动状态?struct User { // ... active: bool,}一开始,这可能看起来不错,但是随

许多具有系统编程背景的Rust初学者倾向于使用bool(甚至u8—8位无符号整数类型)来表示“状态”。HBf28资讯网——每日最新资讯28at.com

例如,如何使用bool来指示用户是否处于活动状态?HBf28资讯网——每日最新资讯28at.com

struct User {    // ...    active: bool,}

HBf28资讯网——每日最新资讯28at.com

一开始,这可能看起来不错,但是随着代码库的增长,会发现“active”不是二进制状态。用户可以处于许多不同的状态,用户可能被挂起或删除。但是,扩展User结构体可能会出现问题,因为代码的其他部分有可能依赖active是bool类型。HBf28资讯网——每日最新资讯28at.com

另一个问题是bool不是自文档化的。active = false是什么意思?用户是否处于非活动状态,或者用户被删除了,或者用户被挂起了?我们不知道!HBf28资讯网——每日最新资讯28at.com

或者,可以使用一个无符号整数来表示状态:HBf28资讯网——每日最新资讯28at.com

struct User {    // ...    status: u8,}

HBf28资讯网——每日最新资讯28at.com

这稍微好一点,因为我们现在可以使用不同的值来表示更多的状态:HBf28资讯网——每日最新资讯28at.com

const ACTIVE: u8 = 0;const INACTIVE: u8 = 1;const SUSPENDED: u8 = 2;const DELETED: u8 = 3;let user = User {    // ...    status: ACTIVE,};

HBf28资讯网——每日最新资讯28at.com

u8的一个常见用例是与C代码交互,在这种情况下,使用u8似乎是唯一的选择。我们还可以将u8包装在一个新类型中!HBf28资讯网——每日最新资讯28at.com

struct User {    // ...    status: UserStatus,}struct UserStatus(u8);const ACTIVE: UserStatus = UserStatus(0);const INACTIVE: UserStatus = UserStatus(1);const SUSPENDED: UserStatus = UserStatus(2);const DELETED: UserStatus = UserStatus(3);let user = User {    // ...    status: ACTIVE,};

HBf28资讯网——每日最新资讯28at.com

这样我们就可以在UserStatus上定义方法:HBf28资讯网——每日最新资讯28at.com

impl UserStatus {    fn is_active(&self) -> bool {        self.0 == ACTIVE.0    }}

HBf28资讯网——每日最新资讯28at.com

我们甚至还可以定义一个构造函数来验证输入:HBf28资讯网——每日最新资讯28at.com

impl UserStatus {    fn new(status: u8) -> Result<Self, &'static str> {        match status {            ACTIVE.0 => Ok(ACTIVE),            INACTIVE.0 => Ok(INACTIVE),            SUSPENDED.0 => Ok(SUSPENDED),            DELETED.0 => Ok(DELETED),            _ => Err("Invalid status"),        }    }}

HBf28资讯网——每日最新资讯28at.com

HBf28资讯网——每日最新资讯28at.com

使用枚举表示状态

HBf28资讯网——每日最新资讯28at.com

枚举是为域内的状态建模的好方法。它们以一种非常简洁的方式表达你的意图。HBf28资讯网——每日最新资讯28at.com

#[derive(Debug)]pub enum UserStatus {    /// 用户是活跃的,可以完全访问他们的帐户和任何相关功能。    Active,    /// 用户的帐户处于非活动状态。该状态可由用户或管理员恢复为激活状态。    Inactive,     /// 该用户的帐户已被暂时暂停,可能是由于可疑活动或违反政策。    /// 在此状态下,用户无法访问其帐户,并且可能需要管理员的干预才能恢复帐户。    Suspended,    /// 该用户的帐号已被永久删除,无法恢复。    /// 与该帐户关联的所有数据都可能被删除,用户需要创建一个新帐户才能再次使用该服务。    Deleted,}

HBf28资讯网——每日最新资讯28at.com

我们可以将这个枚举插入到User结构体中:HBf28资讯网——每日最新资讯28at.com

struct User {    // ...    status: UserStatus,}

HBf28资讯网——每日最新资讯28at.com

但这还不是全部。在Rust中,枚举比许多其他语言强大得多。例如,可以向枚举变量中添加数据:HBf28资讯网——每日最新资讯28at.com

#[derive(Debug)]pub enum UserStatus {    Active,    Inactive,    Suspended { until: DateTime<Utc> },    Deleted { deleted_at: DateTime<Utc> },}

HBf28资讯网——每日最新资讯28at.com

我们还可以表示状态转换:HBf28资讯网——每日最新资讯28at.com

use chrono::{DateTime, Utc};#[derive(Debug)]pub enum UserStatus {    Active,    Inactive,    Suspended { until: DateTime<Utc> },    Deleted { deleted_at: DateTime<Utc> },}impl UserStatus {    /// 暂停用户直到指定日期    fn suspend(&mut self, until: DateTime<Utc>) {        match self {            UserStatus::Active => *self = UserStatus::Suspended { until },            _ => {}        }    }    /// 激活用户    fn activate(&mut self) -> Result<(), &'static str> {        match self {            // A deleted user can't be activated!            UserStatus::Deleted { .. } => return Err("can't activate a deleted user"),            _ => *self = UserStatus::Active        }        Ok(())    }    /// 删除用户,这是一个永久的动作!    fn delete(&mut self) {        if let UserStatus::Deleted { .. } = self {            // 已经删除,不要再设置deleted_at字段。            return;        }        *self = UserStatus::Deleted {            deleted_at: Utc::now(),        }    }    fn is_active(&self) -> bool {        matches!(self, UserStatus::Active)    }    fn is_suspended(&self) -> bool {        matches!(self, UserStatus::Suspended { .. })    }    fn is_deleted(&self) -> bool {        matches!(self, UserStatus::Deleted { .. })    }}#[cfg(test)]mod tests {    use chrono::Duration;    use super::*;    #[test]    fn test_user_status() -> Result<(), &'static str>{        let mut status = UserStatus::Active;        assert!(status.is_active());        // 暂停到明天        status.suspend(Utc::now() + Duration::days(1));        assert!(status.is_suspended());        status.activate()?;        assert!(status.is_active());        status.delete();        assert!(status.is_deleted());        Ok(())    }    #[test]    fn test_user_status_transition() {        let mut status = UserStatus::Active;        assert!(status.is_active());        status.delete();        assert!(status.is_deleted());        // 无法激活已删除的用户        assert!(status.activate().is_err());    }}

HBf28资讯网——每日最新资讯28at.com

看看我们仅仅用几行代码就涵盖了多少内容!我们可以放心地扩展应用程序,因为我们知道不会意外地删除用户两次或重新激活已删除的用户。非法的状态转换现在是不可能的!HBf28资讯网——每日最新资讯28at.com

HBf28资讯网——每日最新资讯28at.com

HBf28资讯网——每日最新资讯28at.com

使用枚举与C代码交互

C代码:HBf28资讯网——每日最新资讯28at.com

typedef struct {    uint8_t status;} User;User *create_user(uint8_t status);

HBf28资讯网——每日最新资讯28at.com

你可以写一个Rust枚举来表示状态:HBf28资讯网——每日最新资讯28at.com

#[repr(u8)]#[derive(Debug, PartialEq)]pub enum UserStatus {    Active = 0,    Inactive,    Suspended,    Deleted,}impl TryFrom<u8> for UserStatus {    type Error = ();    fn try_from(value: u8) -> Result<Self, Self::Error> {        match value {            0 => Ok(UserStatus::Active),            1 => Ok(UserStatus::Inactive),            2 => Ok(UserStatus::Suspended),            3 => Ok(UserStatus::Deleted),            _ => Err(()),        }    }}

HBf28资讯网——每日最新资讯28at.com

注意到#[repr(u8)]属性了吗?它告诉编译器将此枚举表示为无符号8位整数。这对于与C代码的兼容性至关重要。HBf28资讯网——每日最新资讯28at.com

现在,让我们用一个安全的Rust包装器包装C函数:HBf28资讯网——每日最新资讯28at.com

extern "C" {    fn create_user(status: u8) -> *mut User;}pub fn create_user_wrapper(status: UserStatus) -> Result<User, &'static str> {    let user = unsafe { create_user(status as u8) };    if user.is_null() {        Err("Failed to create user")    } else {        Ok(unsafe { *Box::from_raw(user) })    }}

HBf28资讯网——每日最新资讯28at.com

Rust代码现在使用丰富的enum类型与C代码通信。HBf28资讯网——每日最新资讯28at.com

HBf28资讯网——每日最新资讯28at.com

HBf28资讯网——每日最新资讯28at.com

总结

Rust中的枚举比大多数其他语言更强大。它们可以用来优雅地表示状态转换——甚至可以跨越语言边界。HBf28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-81736-0.html一起聊聊在Rust中使用枚举表示状态

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 探索并发安全的Go语言Map - 深入理解Cmap

下一篇: 你有思考过@Transactional事务是真的好用吗?

标签:
  • 热门焦点
  • 天猫精灵Sound Pro体验:智能音箱没有音质?来听听我的

    这几年除了手机作为智能生活终端最主要的核心之外,第二个可以成为中心点的产品是什么?——是智能音箱。 手机在执行命令的时候有两种操作方式,手和智能语音助手,而智能音箱只
  • Rust中的高吞吐量流处理

    作者 | Noz编译 | 王瑞平本篇文章主要介绍了Rust中流处理的概念、方法和优化。作者不仅介绍了流处理的基本概念以及Rust中常用的流处理库,还使用这些库实现了一个流处理程序
  • 2023 年的 Node.js 生态系统

    随着技术的不断演进和创新,Node.js 在 2023 年达到了一个新的高度。Node.js 拥有一个庞大的生态系统,可以帮助开发人员更快地实现复杂的应用。本文就来看看 Node.js 最新的生
  • 这款新兴工具平台,让你的电脑效率翻倍

    随着信息技术的发展,我们获取信息的渠道越来越多,但是处理信息的效率却成为一个瓶颈。于是各种工具应运而生,都在争相解决我们的工作效率问题。今天我要给大家介绍一款效率
  • Temu起诉SHEIN,跨境电商战事升级

    来源 | 伯虎财经(bohuFN)作者 | 陈平安日前据外媒报道,拼多多旗下跨境电商平台Temu正对竞争对手SHEIN提起新诉讼,诉状称Shein&ldquo;利用市场支配力量强迫服装厂商与之签订独家
  • 消费结构调整丨巨头低价博弈,拼多多还卷得动吗?

    来源:征探财经作者:陈香羽随着流量红利的退潮,电商的存量博弈越来越明显。曾经主攻中高端与品质的淘宝天猫、京东重拾&ldquo;低价&rdquo;口号。而过去与他们错位竞争的拼多多,靠
  • 造车两年股价跌六成,小米的估值逻辑变了吗?

    如果从小米官宣造车后的首个交易日起持有小米集团的股票,那么截至2023年上半年最后一个交易日,投资者将浮亏59.16%,同区间的恒生科技指数跌幅为52.78%
  • 超闭合精工铰链 彻底消灭缝隙 三星Galaxy Z Flip5与Galaxy Z Fold5发布

    2023年7月26日,三星电子正式发布了Galaxy Z Flip5与Galaxy Z Fold5。三星新一代折叠屏手机采用超闭合精工铰链,让折叠后的缝隙不再可见。同时,配合处
  • 联想的ThinkBook Plus下一版曝光,键盘旁边塞个平板

    ThinkBook Plus 是联想的一个特殊笔记本类别,它在封面放入了一块墨水屏,也给人留下了较为深刻的印象。据有人爆料,联想的下一款 ThinkBook Plus 可能更特殊,它
Top