Rust学习——TRPL-Part7

Enums and Pattern Matching

Enums are a feature in many languages, but their capabilities differ in each language. Rust’s enums are most similar to algebraic data types(代数数据类型) in functional languages, such as F#, OCaml, and Haskell.

Defining an Enum

We can enumerate all possible variants, which is where enumeration gets its name. 一一列举出

We can express this concept in code by defining an IpAddrKind enumeration and listing the possible kinds an IP address can be, V4 and V6. These are the variants of the enum:

1
2
3
4
enum IpAddrKind {
V4,
V6,
}

Enum Values

Using an enum rather than an enum inside a struct, by putting data directly into each enum variant. (回忆Coq 中Inductive Type中带参)

以 Ipv4 Ipv6 为例,使用enum的好处:

  • 我们直接将数据附加到枚举的每个成员上,这样就不需要一个额外的结构体了。
  • 另外每一个变量可以取不同类型和数量的关联数据,比如你想通过4个 u8数来表示Ipv4 但想通过一个 String 来表示Ipv6。
1
2
3
4
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}

IpAddr在标准库的实现方式:使用枚举和变量来描述,任意一个IpAddr可以是 V4 或者 V6,但它以两个不同的struct形式将地址数据嵌入到变量中,每个变量的定义不同。

可以在枚举变量中嵌入各种类型的数据:字符串、数字类型、struct甚至其他的enum。

The Option Enum and Its Advantages Over Null Values

The Option type is used in many places because it encodes the very common scenario in which a value could be something or it could be nothing.

“Null References: The Billion Dollar Mistake,” Tony Hoare, the inventor of null,

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

Included in the prelude and can be used directly without the Option:: prefix.

Option<T> vs. None

在对 Option<T> 进行 T 的运算之前必须将其转换为 T。通常这能帮助我们捕获到空值最常见的问题之一:假设某值不为空但实际上为空的情况。

不再担心会错误的假设一个非空值,会让你对代码更加有信心。为了拥有一个可能为空的值,你必须要显式的将其放入对应类型的 Option<T> 中。接着,当使用这个值时,必须明确的处理值为空的情况。只要一个值不是 Option 类型,你就 可以 安全的认定它的值不为空。这是 Rust 的一个经过深思熟虑的设计决策,来限制空值的泛滥以增加 Rust 代码的安全性。

Becoming familiar with the methods on Option will be extremely useful in your journey with Rust.

The match Control Flow Operator

因为刚刚提到了硬币,让我们用它们来作为一个使用 match 的例子!我们可以编写一个函数来获取一个未知的硬币,并以一种类似验钞机的方式,确定它是何种硬币并返回它的美分值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#![allow(unused)]
fn main() {
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 Option

我们在之前的部分中使用 Option<T> 时,是为了从 Some 中取出其内部的 T 值;我们还可以像处理 Coin 枚举那样使用 match 处理 Option<T>!只不过这回比较的不再是硬币,而是 Option<T> 的成员,但 match 表达式的工作方式保持不变。

Option<T>本身也是枚举类型,这样就好理解了。

枚举的 exhaustive (穷尽性)以及 _通配符类似ML语言和Coq。

然而,match 在只关心 一个 情况的场景中可能就有点啰嗦了。为此 Rust 提供了if let

Concise Control Flow with if let

if let 语法让我们以一种不那么冗长的方式结合 iflet,来处理只匹配一个模式的值而忽略其他模式的情况。

如下两段代码等价:

1
2
3
4
5
6
let coin = Coin::Penny;
let mut count = 0;
match coin {
Coin::Quarter(state) => println!("State quarter from {:?}!", state),
_ => count += 1,
}

采用 if let

1
2
3
4
5
6
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {:?}!", state);
} else {
count += 1;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!