0%

Rust-枚举与模式匹配

枚举

结构体将字段与数据聚合,而枚举可以将同一类型的东西(同时你可以将它可能的情况列举出来)作为一个集合,使用IP地址举例如下

1
2
3
4
enum IpAddrKind {
v4,
v6,
}

枚举值

可使用::访问枚举值

1
2
let ipv4 = IpAddrKind::v4;
let ipv6 = IpAddrKind::v6;

也可将其传入函数,如func(IpAddrKind::v4)

也可结合结构体使用如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum IpAddrKind {
V4,
V6,
}

struct IpAddr {
kind: IpAddrKind,
address: String,
}

let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};

let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};

也可直接指定枚举值类型如

1
2
3
4
5
6
7
enum IpAddr {
V4(String),
V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));

此时,IpAddr::V4()实际上为一个获取String参数并返回IpAddr类型示例的函数调用,而这些构造函数会被自动定义,枚举可以为每个值指定不同的数据类型,例如

1
2
3
4
5
6
7
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}

let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));

可以将任意类型的数据放入枚举成员中,例如字符串,数字或者结构体,甚至可以嵌套枚举

以下为一个多种成员类型的枚举类型

1
2
3
4
5
6
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}

该枚举类型有四个成员,分别为:

  • Quit 没有关联任何数据。
  • Move 类似结构体包含命名字段。
  • Write 包含单独一个 String
  • ChangeColor 包含三个 i32

如果使用结构体的话,他将会是这样,如果不能使用枚举类型的话,使用结构体需要定义四个不同的结构体类型

1
2
3
4
5
6
7
struct QuitMessage; // 类单元结构体
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // 元组结构体
struct ChangeColorMessage(i32, i32, i32); // 元组结构体

枚举同结构体一样可以使用impl来定义方法

1
2
3
4
5
6
7
8
impl Message {
fn call(&self) {
// 在这里定义方法体
}
}

let m = Message::Write(String::from("hello"));
m.call();

Option枚举

Option是标准库定义的另一个枚举,这个枚举可以表示一个空值,Rust没有nilnullptrNULL那样的空值,取而代之为如下

1
2
3
4
enum Option<T> {
None,
Some(T),
}

可以不使用Option::前缀来直接使用SomeNone,但即便如此Option<T>也是常规的枚举, Some<T>None是其成员,以下是一些例子

1
2
3
4
let some_num = Some(6);
let some_char = Some('a');

let absent_num: Option<i32> = None;

在上述例子中,some_num的类型是Option<i32>some_char的类型是Option<char>,对于absent_num则通过: Option<i32>显示的指明其类型

当有一个Some值时,我们就知道存在一个值,而这个值保存在 Some 中。当有个 None 值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。这样做会比空值更好,因为Option<T>T(这里 T 可以是任何类型)是不同的类型,编译器不允许像一个肯定有效的值那样使用 Option<T>

1
2
3
4
 let x: i8 = 5;
let y: Option<i8> = Some(5);

let sum = x + y; //这两个值不能相加,因为不是同一个类型

match控制流

允许讲一个值与一系列的模式相比较,并根据与相匹配的模式执行相应的代码。模式可以由字面值、变量以及通配符等其他内容构成,以下为一个案例,利用match语法获取对应衣服尺码的袖长

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
enum ClothingSize {
Size_XS,
Szie_S,
Size_M,
Size_L,
Size_XL,
}

fn get_sleeve_len(clothing_size: ClothingSize) -> u8 {
match clothing_size {
ClothingSize::Size_XS => {
println!("This is XS");
10
} //使用 {} 形式时 逗号可写可不写
ClothingSize::Szie_S => 20,
ClothingSize::Size_M => 30,
ClothingSize::Size_L => 40,
ClothingSize::Size_XL => 50,
}
}

绑定值的模式

匹配分支的另一个功能是可以绑定匹配模式的部分值,这也就是如何从枚举成员中提取值的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
enum Brand {
AAA,
BBB
}
enum ClothingSize {
Size_XS,
Szie_S,
Size_M,
Size_L(Brand),
Size_XL,
}

fn get_sleeve_len(clothing_size: ClothingSize) -> u8 {
match clothing_size {
ClothingSize::Size_XS => {
println!("This is XS");
10
} //使用 {} 形式时 逗号可写可不写
ClothingSize::Szie_S => 20,
ClothingSize::Size_M => 30,
ClothingSize::Size_L(brand) => {
println!("clothing brand is {:?}", brand);
40
},
ClothingSize::Size_XL => 50,
}
}

在以上场景中,调用get_sleeve_len(ClothingSize::Size_L(Brand::AAA)),传入函数内部的clothing_size遇到ClothingSize::Size_L(brand)会匹配上,接着就可以直接使用AAA这个值

匹配Option<T>

Option<T>也可以被用作match匹配,例如一个函数,接受一个类型为Option<T>的参数,如果其中有值,则将其加一,如果没有则不进行操作

1
2
3
4
5
6
7
8
9
10
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}

let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);

匹配的穷尽性,通配模式与占位符_

匹配需要列出所有的可能,所有可能出现的值都必须能匹配到,在Rust中可以使用通配模式与_占位符来实现穷尽例如

1
2
3
4
5
6
7
8
9
10
let num = rand();
match num {
7 => deal_seven(),
9 => deal_nine(),
other => deal_other(other),
}

fn deal_seven() {}
fn deal_nine() {}
fn deal_other(num: u8) {}

对于前两个分支,匹配模式是字面值 79,最后一个分支则涵盖了所有其他可能的值,模式是我们命名为 other 的一个变量。other 分支的代码通过将其传递给 deal_other 函数来使用这个变量。

通配模式应该放在最后一位,因为match是按照分支顺序依次匹配的

如果不想使用通配模式获取的值,使用_来替代,其可以匹配任意值而不绑定到该值

1
2
3
4
5
6
7
8
9
10
11
12
let num = rand();
match num {
7 => deal_seven(),
9 => deal_nine(),
_ => deal_other(),
// 如果不想做任何事,可以使用以下写法,返回一个空的元组
_ => (),
}

fn deal_seven() {}
fn deal_nine() {}
fn deal_other() {}

if let简洁控制流

如果有以下类似的逻辑

1
2
3
4
5
let config_max = Some(3u8);
match config_max {
Some(max) => println!("The maximum is configured to be {}", max),
_ => (),
}

仅关心Some得值,其余不做任何操作,可使用if let来简写

1
2
3
4
let config_max = Some(3u8);
if let Some(max) = config_max {
println!("The maximum is configured to be {}", max);
}
-------------本文结束感谢您的阅读-------------