Rust 枚举 简单入门

在计算机领域,总和类型(sum type)长期悲剧性缺位,很多事情却依然行得通,这简直不可思议(参见 Lambda 的缺位)。

------Graydon Hoare

Lambda 在主流编程领域的长期缺位造就了大量"烂"代码,总和类型的缺位同样如此

长期以来被 ML 社区和 Haskell 社区的黑客们称为总和类型、可区分的联合体(union)或代数数据类型。在 Rust 中被称为枚举

定义枚举

Rust 使用 enum 关键字定义枚举类型,例如,定义一个名为 Color 的类型,其值为 RedOrangeYellow

Rust 复制代码
enum Color {
    Red,
    Orange,
    Yellow
}

这声明了一个具有 3 个可能值的 Color 类型,称为变体构造器

使用枚举

创建枚举实例,使用 match 表达式,基于枚举变体进行操作

rust 复制代码
let c1 = Color::Red;
let c2 = Color::Orange;

match c1 {
    Color::Red => println!("Red"),
    Color::Orange => println!("Orange"),
    Color::Yellow => println!("Yellow")
}

带数据的枚举

带数据的枚举允许在每个枚举变体上附加一个或多个值。这些值可以是任何类型,包括基础类型、复合类型,甚至其他枚举类型

rust 复制代码
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32)
}

Message 枚举中

  • Quit 是一个没有关联数据的枚举变体
  • Move 是一个带有两个整数字段 xy 的枚举变体
  • Write 是一个带有一个字符串字段的枚举变体
  • ChangeColor 是一个带有三个整数字段的枚举变体,代表RGB颜色值

使用带数据的枚举

rust 复制代码
let quit_message = Message::Quit;
let move_message = Message::Move { x: 3, y: 4 };
let write_message = Message::Write("Hello".to_string());
let change_color_message = Message::ChangeColor(255, 0, 0);

match quit_message {
    Message::Quit => println!("Quit"),
    Message::Move { x, y } => println!("Move ({}, {})", x, y),
    Message::Write(text) => println!("Write: {}", text),
    Message::ChangeColor(r, g, b) => println!("ChangeColor RGB({}, {}, {})", r, g, b)
}

match move_message {
    Message::Quit => println!("Quit"),
    Message::Move { x, y } => println!("Move ({}, {})", x, y),
    Message::Write(text) => println!("Write: {}", text),
    Message::ChangeColor(r, g, b) => println!("ChangeColor RGB({}, {}, {})", r, g, b)
}
...

枚举也可以有方法,像结构体一样,使用 impl 关键字,基于 new 方法创建的 Message 实例

rust 复制代码
impl Message {
    ...

    fn handle_quit(&self) -> Message {
        // 处理 Quit 逻辑
    }
}

match quit_message {
    Message::Quit => Message::new().handle_quit(),
    ...
}

带数据的枚举在Rust中非常有用,因为允许在单个类型中封装多种不同的行为或状态,同时保持类型安全。经常用于表示命令、事件、消息或其他需要关联数据的等场景

内存中的枚举

在内存中,带有数据的枚举会以一个小型整数标签加上足以容纳最大变体中所有字段的内存块的格式进行存储。标签字段供 Rust 内部使用。它会区分由哪个构造器创建了值,进而决定这个值应该有哪些字段

rust 复制代码
enum RoughTime {
    InThePast(TimeUnit, u32),
    JustNow,
    InTheFuture(TimeUnit, u32),
}

从 Rust 1.50 开始,RoughTime 会占用 8 字节,如图

这里可以对枚举在内存的情况有个大概了解,看不懂可以直接过,一般不影响实战开发

用枚举表示富数据结构

枚举对于快速实现树形数据结构也很有用。假设一个 Rust 程序需要处理任意 JSON 数据。在内存中,任何 JSON 文档都可以表示为这种 Rust 类型的值:

rust 复制代码
use std::collections::HashMap;

enum Json {
    Null,
    Boolean(bool),
    Number(f64),
    String(String),
    Array(Vec<Json>),
    Object(Box<HashMap<String, Json>>),
}

JSON 标准指定了可以出现在 JSON 文档中的不同数据类型:null、布尔值、数值、字符串、各种 JSON 值的数组以及具有字符串键名和 JSON 值的对象

serde_json 是 Rust 的结构体序列化库,是 crates.io 上最常下载的 crate 之一

接口参数,复杂参数一般标配 JSON

这里在表示 ObjectHashMap 周围加 Box 只是为了让所有 Json 值更紧凑。在内存中,Json 类型的值占用 4 个机器字。而 String 值和 Vec 值占用 3 个机器字,Rust 又添加了一个标签字节。Null 值和 Boolean 值中没有足够的数据来用完所有空间,但所有 Json 值的大小必须相同。因此,额外的空间就用不上了。下图展示了 Json 值在内存中的实际布局的一些示例

HashMap 则更大。如果必须在每个 Json 值中为它留出空间,那么将会非常大,在 8 个机器字左右。但是 Box<HashMap> 是 1 个机器字:它只是指向堆中分配的数据的指针。甚至可以通过装箱更多字段来让 Json 更加紧凑

以下是一个表示JSON对象的例子

rust 复制代码
let json = Json::Object(vec![
    ("name".to_string(), Json::String("张三".to_string())),
    ("age".to_string(), Json::Number(30.0)),
    ("is_student".to_string(), Json::Bool(false)),
]);

泛型枚举

泛型枚举可以接受一个或多个类型参数。如 Rust 标准库中的两个例子

rust 复制代码
enum Option<T> {
    Some(T),
    None,
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}

Option枚举有两个变体:Some 和 None。Some 变体包含一个值,这个值的类型是T。T是一个类型参数,它可以是任何类型。使用Option枚举

rust 复制代码
let some_number: Option<i32> = Some(5);
let no_number: Option<i32> = None;

枚举相关内容就这么多了,不同类型的枚举,如何使用,基本操作都已经清楚了,接下来是 Rust 中的 模式

欢迎大家讨论交流,如果喜欢本文章或感觉文章有用,动动你那发财的小手点赞、收藏、关注再走呗 ^_^

微信公众号:草帽Lufei

相关推荐
bing_158几秒前
简单工厂模式 (Simple Factory Pattern) 在Spring Boot 中的应用
spring boot·后端·简单工厂模式
css趣多多19 分钟前
案例自定义tabBar
前端
天上掉下来个程小白25 分钟前
案例-14.文件上传-简介
数据库·spring boot·后端·mybatis·状态模式
Asthenia04121 小时前
基于Jackson注解的JSON工具封装与Redis集成实战
后端
编程星空1 小时前
css主题色修改后会多出一个css吗?css怎么定义变量?
开发语言·后端·rust
林的快手2 小时前
CSS列表属性
前端·javascript·css·ajax·firefox·html5·safari
程序员侠客行2 小时前
Spring事务原理 二
java·后端·spring
匹马夕阳2 小时前
ECharts极简入门
前端·信息可视化·echarts
dmy2 小时前
docker 快速构建开发环境
后端·docker·容器
API_technology3 小时前
电商API安全防护:JWT令牌与XSS防御实战
前端·安全·xss