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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

u8的一个常见用例是与C代码交互,在这种情况下,使用u8似乎是唯一的选择。我们还可以将u8包装在一个新类型中!Pjl28资讯网——每日最新资讯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,};

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

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

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

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

我们甚至还可以定义一个构造函数来验证输入:Pjl28资讯网——每日最新资讯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"),        }    }}

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

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

使用枚举表示状态

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

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

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

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

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

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

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

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

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

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

我们还可以表示状态转换:Pjl28资讯网——每日最新资讯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());    }}

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

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

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

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

使用枚举与C代码交互

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

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

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

你可以写一个Rust枚举来表示状态:Pjl28资讯网——每日最新资讯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(()),        }    }}

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

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

现在,让我们用一个安全的Rust包装器包装C函数:Pjl28资讯网——每日最新资讯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) })    }}

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

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

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

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

总结

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

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

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

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

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

标签:
  • 热门焦点
  • 得物效率前端微应用推进过程与思考

    一、背景效率工程随着业务的发展,组织规模的扩大,越来越多的企业开始意识到协作效率对于企业团队的重要性,甚至是决定其在某个行业竞争中突围的关键,是企业长久生存的根本。得物
  • 深度探索 Elasticsearch 8.X:function_score 参数解读与实战案例分析

    在 Elasticsearch 中,function_score 可以让我们在查询的同时对搜索结果进行自定义评分。function_score 提供了一系列的参数和函数让我们可以根据需求灵活地进行设置。近期
  • 只需五步,使用start.spring.io快速入门Spring编程

    步骤1打开https://start.spring.io/,按照屏幕截图中的内容创建项目,添加 Spring Web 依赖项,并单击“生成”按钮下载 .zip 文件,为下一步做准备。请在进入步骤2之前进行解压。图
  • 零售大模型“干中学”,攀爬数字化珠峰

    文/侯煜编辑/cc来源/华尔街科技眼对于绝大多数登山爱好者而言,攀爬珠穆朗玛峰可谓终极目标。攀登珠峰的商业路线有两条,一是尼泊尔境内的南坡路线,一是中国境内的北坡路线。相
  • 电视息屏休眠仍有网络上传 爱奇艺被质疑“薅消费者羊毛”

    记者丨宁晓敏 见习生丨汗青出品丨鳌头财经(theSankei) 前不久,爱奇艺发布了一份亮眼的一季报,不仅营收和会员营收创造历史最佳表现,其运营利润也连续6个月实现增长。自去年年初
  • 腾讯盖楼,字节拆墙

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之&ldquo;想重温暴刷深渊、30+技能搭配暴搓到爽的游戏体验吗?一起上晶核,即刻暴打!&rdquo;曾凭借直播腾讯旗下代理格斗游戏《DNF》一
  • 大厂卷向扁平化

    来源:新熵作者丨南枝 编辑丨月见大厂职级不香了。俗话说,兵无常势,水无常形,互联网企业调整职级体系并不稀奇。7月13日,淘宝天猫集团启动了近年来最大的人力制度改革,目前已形成一
  • 三星Galaxy Z Fold/Flip 5国行售价曝光 :最低7499元/12999元起

    据官方此前宣布,三星将于7月26日也就是明天在韩国首尔举办Unpacked活动,届时将带来带来包括Galaxy Buds 3、Galaxy Watch 6、Galaxy Tab S9、Galaxy
  • 电博会上海尔智家模拟500平大平层,还原生活空间沉浸式体验

    电博会为了更好地让参展观众真正感受到智能家居的绝妙之处,海尔智家的程传岭先生同样介绍了展会上海尔智家的模拟500平大平层,还原生活空间沉浸式体验。程传
Top