掘金 后端 ( ) • 2024-04-25 09:37

本教程环境

系统:MacOS

Rust 版本:1.77.2

枚举(enums)通过列举可能的成员来定义一个类型。一个常用的枚举叫做 Option

枚举的定义

enum Ordering {
	Less,
	Equal,
	Greater,
}

可以使用 C 风格枚举为各个值存储为整数。 Rust 默认会从 0 开始分配。

enum HttpStatus {
	Ok = 200,
	NotModified = 304,
	NotFound = 404
}

默认,Rust 会使用可容纳它们的最小内置整数类型来存储 C 风格枚举。最适合的是单子节:

use std::mem::size_of;
assert_eq!(size_of::<Ordering>(), 1);
assert_eq!(size_of::<HttpStatus>(), 2);

可向枚举添加 #[repr] 属性来覆盖 Rust 对内存中表示法的默认选择。 可使用 as 将 C 风格的枚举值转换为整数。反之不行。 枚举也可使用 derive 添加各种 crate。并且可以使用 impl 实现自己的方法。

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum TimeUnit {
    Seconds, Minutes, Hours, Days, Months, Years
}

impl TimeUnit {
    fn plural(self) -> &'static str {
        match self {
            TimeUnit::Seconds => "seconds",
            TimeUnit::Minutes => "minutes",
            TimeUnit::Hours => "hours",
            TimeUnit::Days => "days",
            TimeUnit::Months => "months",
            TimeUnit::Years => "years"
        }
    }

    fn sigular(self) -> &'static str {
        self.plural().trim_end_matches('s')
    }
}

枚举可以携带数据

// 刻意四舍五入后的时间戳,所以程序会显示“6个月前”
#[derive(Clone, Copy, Debug, PartialEq)]
enum RoughTime {
    InThePast(TimeUnit, u32),
    JustNow,
    InTheFuture(TimeUnit, u32)
}

上面枚举定义了两个元组型变体。 也可以有结构体变体。

enum Shape {
    Sphere { center: Point3d, radius: f32 },
    cuboid { corner1: Point3d, corner2: Point3d },
}

内存中的枚举

在内存中,带有数据的枚举会以一个小型整数标签加上足以容纳最大变体中所有字段的内存块的格式进行存储。标签字段供 Rust 内部使用。它会区分由哪个构造器创建了值,进而决定这个值应该有哪些字段。 对于上面定义的 RougnTine 枚举,在内存中的值表示如下: 截屏2024-02-29 09.26.05.png

用枚举表示富数据结构

枚举对于快速实现树形数据结构也很有用。例如 JSON 数据。

use std::collections::HashMap;

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

可以在 serde_json crate 中找到类似的枚举。

泛型枚举

Rust 标准库中有两个例子。

enum Option<T> {
	None,
	Some(T)
} 

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

例如,定义一个 BinaryTree 类型:

enum BinaryTree<T> {
	Empty,
	NonEmpty(Box<TreeNode<T>>)
}
struct TreeNode<T> {
	element: T,
	left: BinaryTree<T>,
	right: BinaryTree<T>,
}

math 模式匹配

不通过 . 直接访问。需要使用 match 表达式进行模式匹配。 可以匹配的模式:

  • 字面量;例如 100"name"等;
  • 范围;0..=100256... 等;
  • 通配符;_
  • 变量;
  • 引用变量;ref fieldref mut field
  • 与子模式绑定;var @ 0..=99,使用 @ 左边的变量名,匹配右边的模式
  • 枚举型模式;Some(value)NonePet::Orca
  • 元组型模式:(key, value)
  • 数组型模式:[a, b, c]
  • 切片型模式:[first, second][first, _, third][]
  • 结构体型模式:Color(r, g, b)
  • 引用:&value
  • 或多个模式:'a' | 'A'
  • 守卫表达式: x if x*x <= r2

if let 简洁控制流

if letmatch 的一个语法糖,当它匹配某一个模式时执行代码,忽略其他的模式。

let config_max = Some(3u8);
if let Some(x) = config_max {
    println!("{}", x);
}

引用型模式

Rust 提供两种特性来支持引用。

  • ref 模式会借用已匹配值的一部分。
  • & 模式会匹配引用。

匹配不可复制的值会移动该值。此时需要借用,使用 ref。 与 ref 模式相对的引用模型是 & 模式。ref 是从值取引用,& 从引用取值。

参考链接:

🌟 🙏🙏感谢您的阅读,如果对您有帮助,欢迎关注、点赞 🌟🌟