枚举和模式匹配
定义枚举
定义
枚举就是用来列出所有的值。
enum ipAddrKind {
V4,
V6,
}
fn main() {
let four = ipAddrKind::V4;
let six = ipAddrKind::V6;
}
我们可以结合结构体来写一个简单的案例:
enum ipAddrKind {
V4,
V6,
}
struct IpAddr {
kind: ipAddrKind,
address: String,
}
fn main() {
let home = IpAddr {
kind: ipAddrKind::V4,
address: String::from("127.0.0.1"),
};
}
进一步优化我们可以将这个结构体省略掉,rust 允许我们将类型作为参数传递给枚举,所以我们这里就不需要将枚举再次作为一个结构体的字段了。
#![allow(unused)]
fn main() {
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
}
使用枚举还有一个好处,那就是枚举可以非常轻易地处理多个不同类型的参数:
enum ipAddrKind {
V4(u8, u8, u8, u8),
V6(String),
}
fn main() {
let home = ipAddrKind::V4(127, 0, 0, 1);
}
枚举相对于结构体的一个好处
当我们需要一个统一个方法来处理不同的结构体的时候,我们这时候使用枚举会比较方便。
看官方的例子:
// 这是一个枚举
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
// 这是对应的结构体
struct QuitMessage; // 类单元结构体
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // 元组结构体
struct ChangeColorMessage(i32, i32, i32); // 元组结构体
如果我们要处理这结构体类型很明显我们需要定义四个方法,但是如果对于枚举来说,因为枚举本身是一个类型,所以我们只需要定义一个方法即可。
像下面这样:
impl Message {
fn call(&self) {
// 在这里定义方法体
}
}
let m = Message::Write(String::from("hello"));
m.call();
option 枚举
option 是一个特别常见的枚举,以至于已经被内置到了 preclude 中了,我们甚至可以不需要引入就可以使用。
enum Option<T> {
Some(T),
None,
}
我们来看一个例子,比如有一个数字我们不确定它是否存在我们就可以这样定义:
fn main() {
let x = Some(5);
let y = 10;
let z = x + y;
}
这时候如果我们直接计算 z,即直接将一个类型为 Option(i32) 的值和类型为 i32 的值相加,rust 的编译器就会报错,因为 Option(i32) 类型的值可能为 None,所以我们在相加之前,就需要判断这个值是否为 None,可以像下面这样处理:
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(value) => {
let z = value + y;
println!("z = {}", z)
}
None => {
println!("do nothing")
}
}
}
match 控制流运算符
match 和枚举一起使用
Rust 有一个叫做 match 的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行相应代码。
比如下面是一个关于钱币的例子,我们根据枚举的值返回对应的数字值:
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
fn main() {
let x = value_in_cents(Coin::Penny);
println!("x = {}", x);
}
下面的这个例子也是一样的道理,只不过嵌套了一个枚举:
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
// --snip--
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
}
}
}
fn main() {
value_in_cents(Coin::Quarter(UsState::Alaska));
}
match 和 Option<T>
类型一起使用
我们还是看上面的例子,我们这里就是用 match 处理了为 None 的情况。
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(value) => {
let z = value + y;
println!("z = {}", z)
}
None => {
println!("do nothing")
}
}
}
此外,还有一点,match 是穷尽的,也就是说如果我们只匹配了一部分,神奇的 rust 编译器就会报错。
other 和 _ 占位符
- other 表示处理没有匹配到的剩余的情况,可以获取匹配到的值
- _ 表示匹配所有的情况,一般就放在最下面,无法获取匹配到的值
比如还是刚刚的例子,我们可以使用 other 处理一些情况:
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
// --snip--
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
// other
other => 0,
}
}
fn main() {
let x = value_in_cents(Coin::Quarter(UsState::Alaska));
println!("x: {}", x);
}
if let 简单控制流
if let 我们用来处理只有一个分支需要处理的情况,其他的情况我们使用 else。
fn main() {
let x = Some(3);
// 1
match x {
Some(3) => println!("three"),
_ => println!("anything"),
}
// 2
if let Some(3) = x {
println!("three");
} else {
println!("other");
}
}
这个还是比较好理解的。