06枚举与模式匹配

枚举(enumerations),也被成为 enums。

定义枚举

枚举名命名规范:驼峰格式

枚举的内容称为,成员

  • 枚举的成员位于其标识符的命名空间中,使用::(两个冒号)分开。
  • 枚举成员可以附加数据,使用()圆括号。
    • 可以将任意类型的数据放入枚举成员中
      • 例如,字符串、数字类型或者结构体,甚至还可以包含另一个枚举
  • 可以使用 impl 在枚举上定义方法

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
enum IpAddrKind {
V4,
V6,
}

// 直接访问
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

// 通过函数获取
fn route(ip_type: IpAddrKind) {}
route(IpAddrKind::V4);
route(IpAddrKind::V6);

成员包含多种类型:

1
2
3
4
5
6
enum Message {
Quit, // 没有关联任何数据
Move { x: i32, y: i32 }, // 包含一个匿名结构体
Write(String), // 包含单独一个 `String`
ChangeColor(i32, i32, i32), 包含三个`i32`
}

在枚举上定义方法:

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

// 实例化一个Message枚举,并调用call方法
let m = Message::Write(String::from("hello"));
m.call();

使用枚举时需要注意:

  1. 枚举成员的类型,是枚举本身;
  2. 同一枚举成员的类型,是相同的;
  3. (与 Java 一样)

在 Rust 中,如果定义一个枚举,任何值只能是一个枚举的成员,且枚举的成员从根本上将仍是枚举,所以使用枚举中任何成员时,都应该看做是相同的类型。

  • 例如:IP地址要么是IPv4,要么是IPv6,只能是其中之一,且IPv4和IPv6都是IP地址,所以任何类型的IP地址都是看做相同的类型;
  • 举例2:Result<T,E>,无论是 Ok(T) ,还是 Err(E),它们本质上都是一种结果。所以在使用时,Ok 和 Err 是相同类型,即 Result。

Option 枚举和其相对于空值的优势

空值(Null)是一个值,它代表没有值。

  • 空值的问题在于当你尝试像一个非空值那样使用一个空值,会出现某种形式的错误。因为空和非空的属性无处不在,非常容易出现这类错误。

空值表达的概念是有意义的:空值是一个因为某种原因目前无效或缺失的值。

Rust 中没有空值。不过拥有一个可以编码存在或不存在概念的枚举。

  • Optin<T>
1
2
3
4
enum Option<T> {
Some<T>,
None,
}
  • 定义于标准库中,并被包含在 prelude 之中,不需要显示引入作用域;
  • 它的成员可以不需要Option:: 前缀,可以直接使用 SomeNone
  • Some<T> 中的 <T> 语法是 Rust 的泛型
1
2
3
4
5
6
let some_number = Some(5);
let some_string = Some("a string");

// 如果使用 None 而不是 Some,需要告诉 Rust `Option<T>` 是什么类型
// 因为编译器无法根据 None 值推断出 Some 成员保存的值的类型;
let absent_number: Option<i32> = None;

注: Option<T>T (这里的 T 可以是任何类型)是不同的类型。

如何使用

  1. 当想拥有一个可能为空的值时,显示的将其放入对应的类型的Option<T> 中;

  2. 当使用该值时,必须明确的而处理值为空的情况。

    只要一个值不是Option<T> 类型,就可以安全的认定它的值不为空。

Rust 这个设计可以限制空值的泛滥以增加 Rust 的安全性。

TODO: 记录几个常用的 Option<T> 的方法

枚举常常和 match 一起使用。

match 控制流运算符

match 是控制流运算符 (有点类似于 Java 的 switch,与scala 中的 match 极其相似)

1
2
3
4
5
6
7
match 表达式 {
模式 => 表达式, // 表达式的结果值将作为整个 match 表达式的返回值;
模式2 => { // 分支代码比较短,通常不使用大括号,长的则使用大括号;
表达式2
},
...
}

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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,
}
}

绑定值的模式

不管枚举成员是否包含值, 都可以进行 match 模式匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
},
}
}

匹配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 中 match 匹配是穷尽的(exhaustive)

  • 必须穷举到最后的可能性来使代码有效
  • Rust 编译器会帮我们检查

通配模式和 _ 占位符

Rust 提供了一个通配模式,和一个使用_的特殊模式,用于不想列举出所有可能值的场景。

通配模式

  1. 将匹配所有未被列出的值;(满足了match必须被穷尽的要求)
  2. 通配分支要放在最后;(后面不能有其他分支,有也不会被匹配到)
  3. 可以使用自定义的变量;
1
2
3
4
5
6
7
8
9
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
other => move_player(other), // other分支,通过将 other传递给函数来使用这个变量。
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(num_spaces: u8) {}

_ 占位符模式

  1. 与通配模式的不同点:匹配后的表达式中不能使用_
    • _占位符不能被用于模式右边的表达式中;(告诉 Rust 我们不会使用该值,所以 Rust 也不会警告我们存在未使用的变量)
  2. 其余特点与通配模式一样;
1
2
3
4
5
6
7
8
let some_u8_value = 0u8;
match some_u8_value {
1 => println!("one"),
3 => println!("three"),
5 => println!("five"),
7 => println!("seven"),
_ => (), // 剩下的所有可能在这里,() 表示 unit 值,就是什么也不做;
}

if let 简洁控制流

当我们只关心匹配一个模式分支的时候,可以使用 if let 表达式代替 match

疑问:直接使用 if 表达式不是也可以吗?为什么要单独创建 if let 表达式?

语法:

1
2
3
4
5
if let pattern = value {   // 这里是关心匹配的模式
// something
} else { // else 中的内容与 match 的 _ 通配符效果一样,即剩下的所有模式;
// another
}

if letmatch 的一个语法糖

1
2
3
4
5
# let some_u8_value = Some(0u8);
// if let 的方式与 match 行为一致
if let Some(3) = some_u8_value {
println!("three");
}
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2023 ligongzhao
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信