这篇博客涉及到的知识点有 枚举的定义、Option 枚举、match 语法、if let 语法。
枚举的定义
Rust 中定义一个枚举,使用关键字 enum
,看下面的代码
// 定义一个NPC类型枚举
enum NPCType {
TalkNPC,
WalkNPC,
BattleNPC,
TaskNPC,
}
enum GameState {
OnLogin = 1,
OnHall = 2,
OnWorld,
OnBattle,
OnLogOut,
}
但是 Rust 的枚举是可以关联数据的,看下面的代码
enum CommandType {
Move(i32, i32, i32),
Jump(u32),
ChangeColor(u8, u8, u8, u8),
Smile,
}
fn main() {
let move_command = CommandType::Move(10, -5, 3);
let jump_command = CommandType::Jump(2);
let change_color_command = CommandType::ChangeColor(110,128,55,255);
let smile_command = CommandType::Smile;
}
上面的代码中,可以给枚举中的字段关联数据,例如,Move 关联了3个i32类型的数据。我们可以关联任何类型的数据,也可以是自己定义的结构体,或者另一个枚举,都是可以的。
Option 枚举
Rust 没有空值(Null),但是可以使用 Option 枚举,来表示有和没有的概念。Option 枚举有两个字段,一个是 Some, 一个是 None,而 Option 是泛型的,就是说它可以面向任何类型的值 Option<T>
,用于表示任何类型的值,有值,或者没有值。Some(T) 表示有值,而 None 表示没有值,以 i32 为例子,看下面的代码
fn main() {
let has_value: Option<i32> = Some(200);
let none_value: Option<i32> = None;
println!("has_value: {:?}", has_value);
println!("none_value: {:?}", none_value);
}
Option 可以理解为盒子,Some(T) 表示有盒子,如果有盒子,那盒子里一定装了某种东西,也就是某种类型的值。而 None 表示没有盒子。上面的代码中,直接使用了 Some(200)
,而不是 Option::Some(200)
,是因为太常用,而被 Rust 包含在了 preload 中,所以我们可以省略前缀 Option::
。在 Rust 的学习过程中,会经常遇到 Option。特别是函数的返回值。
fn main() {
let girl_age = get_girl_age(true);
println!("girl_age: {:?}", girl_age); // Some(18)
let boy_age = get_girl_age(false);
println!("boy_age: {:?}", boy_age); // None
}
fn get_girl_age(is_girl: bool) -> Option<u32> {
if is_girl {
Some(18)
} else {
None
}
}
match 语法
match 语法有点类型其他语言的 switch 语法,用于条件匹配。直接看代码
enum CommandType {
Move(i32, i32, i32),
Jump(u32),
ChangeColor(u8, u8, u8, u8),
Smile,
}
fn main() {
let move_command = CommandType::Move(10, 5, 2);
process_command(&move_command);
let change_color_command = CommandType::ChangeColor(154,220, 14, 255);
process_command(&change_color_command);
}
fn process_command(command: &CommandType) {
match command {
CommandType::Move(x, y, z) => {
println!("Player Move, x: {} y: {} z: {}", x, y, z);
},
CommandType::Jump(height) => {
println!("Player Jump to: {}", height);
},
CommandType::ChangeColor(r, g, b, a) => {
println!("player change color: ({}, {}, {}, {})", r, g, b, a);
},
CommandType::Smile => {
println!("player smile");
}
}
}
上面的代码中,还是以 CommandType 枚举来举例。在 main 函数中定义了两个 CommandType 变量,分别使用不同的枚举,然后在 process_command 中,使用 match 语法匹配。在使用 match 匹配时, 一但类型匹配成功,就可以将其关联的值,绑定到某个变量上,然后在 match 逻辑中使用。例如上面代码中匹配上 CommandType::Move,Move 关联了三个 i32 类型的数据,所以在匹配时,就可以将关联的数据,绑定到 match 中的 x, y, z 三个变量上。
Rust 的 match 匹配必须覆盖所有的类型,如果逻辑中只关心某些类型,则可以使用 _
忽略其他类型。
enum CommandType {
Move(i32, i32, i32),
Jump(u32),
ChangeColor(u8, u8, u8, u8),
Smile,
}
fn main() {
let command = CommandType::Smile;
match command {
CommandType::Smile => {
println!("Smile Command");
},
_ => {
println!("Other Command");
}
}
}
if let 语法
相比于 match 匹配的繁琐,if let 语法让匹配更简洁一些
enum CommandType {
Move(i32, i32, i32),
Jump(u32),
ChangeColor(u8, u8, u8, u8),
Smile,
}
fn main() {
let move_command = CommandType::Move(10, 5, 2);
if let CommandType::Move(x, y, z) = move_command {
println!("Player Move: x: {}, y: {}, z: {}", x, y, z);
} else {
println!("is not move command");
}
}
上面的代码中,if let 可以理解为,将右边的 move_command 和左边的枚举类型匹配,如果能匹配上,则将 move_command中的数据绑定到 x, y, z 上,然后执行匹配成功的逻辑,也就是打印出 x, y, z 的值,如果类型匹配不上,则执行匹配不上的逻辑。
为了更好的理解,下面再看一个例子
fn main() {
let x = 2;
if x == 2 {
println!("x = 2");
} else {
println!("x != 2");
}
if let 2 = x {
println!("x = 2");
} else {
println!("x != 2");
}
}
上面代码中 if 语句 和 if let 语句执行的打印是一样的,似乎是一个正向匹配测试,一个反向匹配测试的感觉。