通过对《Rust 程序设计语言》,《通过例子学 Rust 中文版》以及令狐一冲老师对相关知识点的学习总结而成。
rust -枚举和模式匹配学习
枚举(enumerations),也被称作 enums。枚举允许你通过列举可能的 成员(variants) 来定义一个类型。首先,我们会定义并使用一个枚举来展示它是如何连同数据一起编码信息的。接下来,我们会探索一个特别有用的枚举,叫做 Option,它代表一个值要么是某个值要么什么都不是。然后会讲到在 match 表达式中用模式匹配,针对不同的枚举值编写相应要执行的代码。最后会介绍 if let,另一个简洁方便处理代码中枚举的结构。
1 类似于C语言的方式定义
可以通过在代码中定义一个 IpAddrKind 枚举来表现这个概念并列出可能的 IP 地址类型,V4 和 V6。这被称为枚举的 成员(variants):
//枚举的定义
enum IpAddrKind {
V4,
V6,
}
//定义一个IP地址的结构体
struct IpAddr {
kind: IpAddrKind,
address: String,
}
//定义home变量去声明home IP地址的相关信息
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
//定义loopback变量去声明环回口IP地址的相关信息
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
我们定义了一个有两个字段的结构体 IpAddr:IpAddrKind类型的 kind 字段和 String 类型 address 字段。我们有这个结构体的两个实例。
- 第一个,home,它的 kind 的值是 IpAddrKind::V4 与之相关联的地址数据是 127.0.0.1。
- 第二个实例,loopback,kind 的值是 IpAddrKind 的另一个成员,V6,关联的地址是 ::1。
我们使用了一个结构体来将 kind 和 address 打包在一起,现在枚举成员就与值相关联了。
2 rust语言提倡的方式定义
我们可以使用一种更简洁的方式来表达相同的概念,仅仅使用枚举并将数据直接放进每一个枚举成员而不是将枚举作为结构体的一部分。IpAddr 枚举的新定义表明了 V4 和 V6 成员都关联了 String 值:
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
3 可以是不同类型
用枚举替代结构体还有另一个优势:每个成员可以处理不同类型和数量的数据。IPv4 版本的 IP 地址总是含有四个值在 0 和 255 之间的数字部分。如果我们想要将 V4 地址存储为四个 u8 值而 V6 地址仍然表现为一个 String,这就不能使用结构体了。枚举则可以轻易地处理这个情况:
#![allow(unused)]
fn main() {
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
}
代码展示了使用枚举来存储两种不同 IP 地址的几种可能的选择。然而,事实证明存储和编码 IP 地址实在是太常见了以致标准库提供了一个开箱即用的定义!让我们看看标准库是如何定义 IpAddr 的:它正有着跟我们定义和使用的一样的枚举和成员,不过它将成员中的地址数据嵌入到了两个不同形式的结构体中,它们对不同的成员的定义是不同的:
struct Ipv4Addr {
// --snip--
}
struct Ipv6Addr {
// --snip--
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
4 经典用法
我们定义一个 Message 枚举,其每个成员都存储了不同数量和类型的值
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
这个枚举有四个含有不同类型的成员:
- Quit 没有关联任何数据。等同于struct QuitMessage 类单元结构体
- Move 包含一个匿名结构体。等同于struct MoveMessage {
x: i32
y: i32
} - Write 包含单独一个 String。等同于struct WriteMessage(String)
- ChangeColor 包含三个 i32。等同于struct ChangeColorMessage(i32, i32, i32);
上面所定义的枚举就等同于下面的定义如下这些结构体可以包含与之前枚举成员中相同的数据:
struct QuitMessage; // 类单元结构体
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // 元组结构体
struct ChangeColorMessage(i32, i32, i32); // 元组结构体
5 枚举类型的方法以及match
impl Message {
fn print(&self) {
match *self {
Message::Quit => println!("quit"),
Message::Move{x, y} => println!("Move x = {}, y = {}", x, y),
Message::ChangeColor(x, y, z) => println!("ChangeColor x = {}, y = {}, z = {}", x, y, z),
_ => println!("Write and others"),
}
}
}
let q = Message::Quit;
q.print();
let m = Message::Move(1, 2);
m.print();
let w = Message::Write(String::from("hello"));
w.print();
let c = Message::ChangeColor(1, 2, 3);
c.print();
参考文献
- 《Rust 程序设计语言》
- 《通过例子学 Rust中文版》
- 令狐一冲老师对相关知识点的学习总结而成。