From 300bffb67b945fc4f77417ed44503c3b6172cd05 Mon Sep 17 00:00:00 2001 From: yang yue Date: Sun, 19 Feb 2017 23:29:48 +0800 Subject: [PATCH] wip --- docs/ch01-00-introduction.html | 2 +- docs/ch01-01-installation.html | 2 +- docs/ch01-02-hello-world.html | 2 +- docs/ch02-00-guessing-game-tutorial.html | 2 +- docs/ch03-00-common-programming-concepts.html | 2 +- docs/ch03-01-variables-and-mutability.html | 2 +- docs/ch03-02-data-types.html | 2 +- docs/ch03-03-how-functions-work.html | 2 +- docs/ch03-04-comments.html | 2 +- docs/ch03-05-control-flow.html | 2 +- docs/ch04-00-understanding-ownership.html | 2 +- docs/ch04-01-what-is-ownership.html | 2 +- docs/ch04-02-references-and-borrowing.html | 2 +- docs/ch04-03-slices.html | 2 +- docs/ch05-00-structs.html | 2 +- docs/ch05-01-method-syntax.html | 2 +- docs/ch06-00-enums.html | 2 +- docs/ch06-01-defining-an-enum.html | 2 +- docs/ch06-02-match.html | 165 ++++++++++++- docs/ch06-03-if-let.html | 22 +- docs/index.html | 2 +- docs/print.html | 185 +++++++++++++- src/SUMMARY.md | 2 +- src/ch06-02-match.md | 230 +++++++++++++++++- src/ch06-03-if-let.md | 27 +- 25 files changed, 640 insertions(+), 29 deletions(-) diff --git a/docs/ch01-00-introduction.html b/docs/ch01-00-introduction.html index bb21ba6..07a5ee3 100644 --- a/docs/ch01-00-introduction.html +++ b/docs/ch01-00-introduction.html @@ -47,7 +47,7 @@
diff --git a/docs/ch01-01-installation.html b/docs/ch01-01-installation.html index 9d90814..60d9a13 100644 --- a/docs/ch01-01-installation.html +++ b/docs/ch01-01-installation.html @@ -47,7 +47,7 @@
diff --git a/docs/ch01-02-hello-world.html b/docs/ch01-02-hello-world.html index fd14965..5146379 100644 --- a/docs/ch01-02-hello-world.html +++ b/docs/ch01-02-hello-world.html @@ -47,7 +47,7 @@
diff --git a/docs/ch02-00-guessing-game-tutorial.html b/docs/ch02-00-guessing-game-tutorial.html index 551bf29..1b34d5d 100644 --- a/docs/ch02-00-guessing-game-tutorial.html +++ b/docs/ch02-00-guessing-game-tutorial.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-00-common-programming-concepts.html b/docs/ch03-00-common-programming-concepts.html index ecf7e8f..5d94f47 100644 --- a/docs/ch03-00-common-programming-concepts.html +++ b/docs/ch03-00-common-programming-concepts.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-01-variables-and-mutability.html b/docs/ch03-01-variables-and-mutability.html index 25651e4..7dcad96 100644 --- a/docs/ch03-01-variables-and-mutability.html +++ b/docs/ch03-01-variables-and-mutability.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-02-data-types.html b/docs/ch03-02-data-types.html index bd72965..d9dcd3e 100644 --- a/docs/ch03-02-data-types.html +++ b/docs/ch03-02-data-types.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-03-how-functions-work.html b/docs/ch03-03-how-functions-work.html index 24cebe3..adc739a 100644 --- a/docs/ch03-03-how-functions-work.html +++ b/docs/ch03-03-how-functions-work.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-04-comments.html b/docs/ch03-04-comments.html index 1d7aa4c..6ed8727 100644 --- a/docs/ch03-04-comments.html +++ b/docs/ch03-04-comments.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-05-control-flow.html b/docs/ch03-05-control-flow.html index b8390f4..6ac1459 100644 --- a/docs/ch03-05-control-flow.html +++ b/docs/ch03-05-control-flow.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-00-understanding-ownership.html b/docs/ch04-00-understanding-ownership.html index 4bb45f1..30f18e3 100644 --- a/docs/ch04-00-understanding-ownership.html +++ b/docs/ch04-00-understanding-ownership.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-01-what-is-ownership.html b/docs/ch04-01-what-is-ownership.html index 6964d3b..19e42ed 100644 --- a/docs/ch04-01-what-is-ownership.html +++ b/docs/ch04-01-what-is-ownership.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-02-references-and-borrowing.html b/docs/ch04-02-references-and-borrowing.html index d3ebc26..d5b074f 100644 --- a/docs/ch04-02-references-and-borrowing.html +++ b/docs/ch04-02-references-and-borrowing.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-03-slices.html b/docs/ch04-03-slices.html index df385c0..c869382 100644 --- a/docs/ch04-03-slices.html +++ b/docs/ch04-03-slices.html @@ -47,7 +47,7 @@
diff --git a/docs/ch05-00-structs.html b/docs/ch05-00-structs.html index a758e2a..ce210a7 100644 --- a/docs/ch05-00-structs.html +++ b/docs/ch05-00-structs.html @@ -47,7 +47,7 @@
diff --git a/docs/ch05-01-method-syntax.html b/docs/ch05-01-method-syntax.html index e4694cb..5d6881a 100644 --- a/docs/ch05-01-method-syntax.html +++ b/docs/ch05-01-method-syntax.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-00-enums.html b/docs/ch06-00-enums.html index edbf4e2..46acef1 100644 --- a/docs/ch06-00-enums.html +++ b/docs/ch06-00-enums.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-01-defining-an-enum.html b/docs/ch06-01-defining-an-enum.html index f665615..742b3bc 100644 --- a/docs/ch06-01-defining-an-enum.html +++ b/docs/ch06-01-defining-an-enum.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-02-match.html b/docs/ch06-02-match.html index dc9a902..456cc6f 100644 --- a/docs/ch06-02-match.html +++ b/docs/ch06-02-match.html @@ -47,7 +47,7 @@
@@ -73,7 +73,168 @@
commit 396e2db4f7de2e5e7869b1f8bc905c45c631ad7d

-

Rust 有一个叫做match的极为强大的控制流运算符,

+

Rust 有一个叫做match的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较并根据匹配的模式执行代码。模式可由字面值、变量、通配符和许多其他内容构成;第十八章会讲到所有不同种类的模式以及他们的作用。match的力量来源于模式的表现力以及编译器检查,它确保了所有可能的情况都得到处理。

+

match表达式想象成某种硬币分啦机:硬币滑入有着不同大小孔洞的轨道,每一个硬币都会掉入符合它大小的孔洞。同样地,值也会检查match的每一个模式,并且在遇到第一个“符合”的模式时,值会进入相关联的代码块并在执行中被使用。

+

因为刚刚提到了硬币,让我们用他们来作为一个使用match的例子!我们可以编写一个函数来获取一个未知的(美国)硬币,并以一种类似验钞机的方式,确定它是何种硬币并返回它的美分值,如列表 6-3 中所示:

+
+
enum Coin {
+    Penny,
+    Nickel,
+    Dime,
+    Quarter,
+}
+
+fn value_in_cents(coin: Coin) -> i32 {
+    match coin {
+        Coin::Penny => 1,
+        Coin::Nickel => 5,
+        Coin::Dime => 10,
+        Coin::Quarter => 25,
+    }
+}
+
+
+

Listing 6-3: An enum and a match expression that has the variants of the enum +as its patterns.

+
+
+

拆开value_in_cents函数中的match来看。首先,我们列出match关键字后跟一个表达式,在这个例子中是coin的值。这看起来非常像if使用的表达式,不过这里有一个非常大的区别:对于if,表达式必须返回一个布尔值。而这里它可以是任何类型的。例子中的coin的类型是列表 6-3 中定义的Coin枚举。

+

接下来是match的分支。一个分支有两个部分:一个模式和一些代码。第一个分支的模式是值Coin::Penny而之后的=>运算符将模式和将要运行的代码分开。这里的代码就仅仅是值1。每一个分支之间使用逗号分隔。

+

每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个match表达式的返回值。

+

如果分支代码较短的话可以不适用大括号,正如列表 6-3 中的每个分支都只是返回一个值。如果想要在分支中运行多行代码,可以使用大括号。例如,如下代码在每次使用Coin::Penny调用时都会打印出“Lucky penny!”,同时仍然返回代码块最后的值,1

+
# enum Coin {
+#    Penny,
+#    Nickel,
+#    Dime,
+#    Quarter,
+# }
+#
+fn value_in_cents(coin: Coin) -> i32 {
+    match coin {
+        Coin::Penny => {
+            println!("Lucky penny!");
+            1
+        },
+        Coin::Nickel => 5,
+        Coin::Dime => 10,
+        Coin::Quarter => 25,
+    }
+}
+
+

绑定值的模式

+

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

+

作为一个例子,让我们修改枚举的一个成员来存放数据。1999 年到 2008 年间,美帝在 25 美分的硬币的一侧为 50 个州每一个都印刷了不同的设计。其他的硬币都没有这种区分州的设计,所以只有这些 25 美分硬币有特殊的价值。可以将这些信息加入我们的enum,通过改变Quarter成员来包含一个State值,列表 6-4 中完成了这些修改:

+
+
#[derive(Debug)] // So we can inspect the state in a minute
+enum UsState {
+    Alabama,
+    Alaska,
+    // ... etc
+}
+
+enum Coin {
+    Penny,
+    Nickel,
+    Dime,
+    Quarter(UsState),
+}
+
+
+

Listing 6-4: A Coin enum where the Quarter variant also holds a UsState +value

+
+
+

想象一下我们的一个朋友尝试收集所有 50 个州的 25 美分硬币。在根据硬币类型分类零钱的同时,也可以报告出每个 25 美分硬币所对应的州名称,这样如何我们的朋友没有的话,他可以把它加入收藏。

+

在这些代码的匹配表达式中,我们在匹配Coin::Quarter成员的分支的模式中增加了一个叫做state的变量。当匹配到Coin::Quarter时,变量state将会绑定 25 美分硬币所对应州的值。接着在代码那个分支中使用state,如下:

+
# #[derive(Debug)]
+# enum UsState {
+#    Alabama,
+#    Alaska,
+# }
+#
+# enum Coin {
+#    Penny,
+#    Nickel,
+#    Dime,
+#    Quarter(UsState),
+# }
+#
+fn value_in_cents(coin: Coin) -> i32 {
+    match coin {
+        Coin::Penny => 1,
+        Coin::Nickel => 5,
+        Coin::Dime => 10,
+        Coin::Quarter(state) => {
+            println!("State quarter from {:?}!", state);
+            25
+        },
+    }
+}
+
+

如果调用value_in_cents(Coin::Quarter(UsState::Alaska))coin将是Coin::Quarter(UsState::Alaska)。当将值与每个分支相比较时,没有分支会匹配知道遇到Coin::Quarter(state)。这时,state绑定的将会是值UsState::Alaska。接着就可以在println!表达式中使用这个绑定了,像这样就可以获取Coin枚举的Quarter成员中内部的州的值。

+

匹配Option<T>

+

在之前的部分在使用Option<T>时我们想要从Some中取出其内部的T值;也可以像处理Coin枚举那样使用match处理Option<T>!与其直接比较硬币,我们将比较Option<T>的成员,不过match表达式的工作方式保持不变。

+

比如想要编写一个函数,它获取一个Option<i32>并且如果其中有一个值,将其加一。如果其中没有值,函数应该返回None值并不尝试执行任何操作。

+

编写这个函数非常简单,得益于match,它将看起来像列表 6-5 中这样:

+
+
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);
+
+
+

Listing 6-5: A function that uses a match expression on an Option<i32>

+
+
+

匹配Some(T)

+

更仔细的检查plus_one的第一行操作。当调用plus_one(five)时,plus_one函数体中的x将会是值Some(5)。接着将其与每个分支比较。

+
None => None,
+
+

Some(5)并不匹配模式None,所以继续进行下一个分支。

+
Some(i) => Some(i + 1),
+
+

Some(5)Some(i)匹配吗?为什么不呢!他们是相同的成员。i绑定了Some中包含的值,所以i的值是5。接着匹配分支的代码被执行,所以我们将i的值加一并返回一个含有值6的新Some

+

匹配None

+

接着考虑下列表 6-5 中plus_one的第二个调用,这里xNone。我们进入match并与第一个分支相比较。

+
None => None,
+
+

匹配上了!这里没有值来加一,所以程序结束并返回=>右侧的值None,因为第一个分支就匹配到了,其他的分支将不再比较。

+

match与枚举相结合在很多场景中都是有用的。你会在 Rust 代码中看到很多这样的模式:match一个枚举,绑定其中的值到一个变量,接着根据其值执行代码。这在一开有点复杂,不过一旦习惯了,你将希望所有语言都拥有它!这一直是用户的最爱。

+

匹配是穷尽的

+

match还有另一方面需要讨论。考虑一下plus_one函数的这个版本:

+
fn plus_one(x: Option<i32>) -> Option<i32> {
+    match x {
+        Some(i) => Some(i + 1),
+    }
+}
+
+

我们没有处理None的情况,所以这些代码会造成一个 bug。幸运的是,这是一个 Rust 知道如何处理的 bug。如果尝试编译这段代码,会得到这个错误:

+
error[E0004]: non-exhaustive patterns: `None` not covered
+ -->
+  |
+6 |         match x {
+  |               ^ pattern `None` not covered
+
+

Rust 知道我们没有覆盖所有可能的情况甚至知道那些模式被忘记了!Rust 中的匹配是穷尽的(*exhaustive):必须穷举到最后的可能性来使代码有效。特别的在这个Option<T>的例子中,Rust 防止我们忘记明确的处理None的情况,这使我们免于假设拥有一个实际上为空的值,这造成了之前提到过的价值亿万的错误。

+

_通配符

+

Rust 也提供了一个模式用于不想列举出所有可能值的场景。例如,u8可以拥有 0 到 255 的有效的值,如果我们只关心 1、3、5 和 7 这几个值,就并不想必须列出 0、2、4、6、8、9 一直到 255 的值。所幸我们不必这么做:可以使用特殊的模式_替代:

+
let some_u8_value = 0u8;
+match some_u8_value {
+    1 => println!("one"),
+    3 => println!("three"),
+    5 => println!("five"),
+    7 => println!("seven"),
+    _ => (),
+}
+
+

_模式会匹配所有的值。通过将其放置于其他分支之后,_将会匹配所有之前没有指定的可能的值。()就是 unit 值,所以_的情况什么也不会发生。因此,可以说我们想要对_通配符之前没有列出的所有可能的值不做任何处理。

+

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

diff --git a/docs/ch06-03-if-let.html b/docs/ch06-03-if-let.html index 0b683ae..433e8d7 100644 --- a/docs/ch06-03-if-let.html +++ b/docs/ch06-03-if-let.html @@ -47,7 +47,7 @@
@@ -67,7 +67,25 @@
-

Concise Control Flow with if let

+

if let简单控制流

+
+

ch06-03-if-let.md +
+commit 396e2db4f7de2e5e7869b1f8bc905c45c631ad7d

+
+

if let语法让我们以一种不那么冗长的方式结合iflet,来处理匹配一个模式的值而忽略其他的值。考虑列表 6-6 中的程序,它匹配一个Option<u8>值并只希望当值是三时执行代码:

+
+
let some_u8_value = Some(0u8);
+match some_u8_value {
+    Some(3) => println!("three"),
+    _ => (),
+}
+
+
+

Listing 6-6: A match that only cares about executing code when the value is +Some(3)

+
+
diff --git a/docs/index.html b/docs/index.html index 29ca5d8..4fec122 100644 --- a/docs/index.html +++ b/docs/index.html @@ -46,7 +46,7 @@
diff --git a/docs/print.html b/docs/print.html index f9ced60..eceecbd 100644 --- a/docs/print.html +++ b/docs/print.html @@ -47,7 +47,7 @@
@@ -2867,8 +2867,187 @@ not satisfied
commit 396e2db4f7de2e5e7869b1f8bc905c45c631ad7d

-

Rust 有一个叫做match的极为强大的控制流运算符,

-

Concise Control Flow with if let

+

Rust 有一个叫做match的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较并根据匹配的模式执行代码。模式可由字面值、变量、通配符和许多其他内容构成;第十八章会讲到所有不同种类的模式以及他们的作用。match的力量来源于模式的表现力以及编译器检查,它确保了所有可能的情况都得到处理。

+

match表达式想象成某种硬币分啦机:硬币滑入有着不同大小孔洞的轨道,每一个硬币都会掉入符合它大小的孔洞。同样地,值也会检查match的每一个模式,并且在遇到第一个“符合”的模式时,值会进入相关联的代码块并在执行中被使用。

+

因为刚刚提到了硬币,让我们用他们来作为一个使用match的例子!我们可以编写一个函数来获取一个未知的(美国)硬币,并以一种类似验钞机的方式,确定它是何种硬币并返回它的美分值,如列表 6-3 中所示:

+
+
enum Coin {
+    Penny,
+    Nickel,
+    Dime,
+    Quarter,
+}
+
+fn value_in_cents(coin: Coin) -> i32 {
+    match coin {
+        Coin::Penny => 1,
+        Coin::Nickel => 5,
+        Coin::Dime => 10,
+        Coin::Quarter => 25,
+    }
+}
+
+
+

Listing 6-3: An enum and a match expression that has the variants of the enum +as its patterns.

+
+
+

拆开value_in_cents函数中的match来看。首先,我们列出match关键字后跟一个表达式,在这个例子中是coin的值。这看起来非常像if使用的表达式,不过这里有一个非常大的区别:对于if,表达式必须返回一个布尔值。而这里它可以是任何类型的。例子中的coin的类型是列表 6-3 中定义的Coin枚举。

+

接下来是match的分支。一个分支有两个部分:一个模式和一些代码。第一个分支的模式是值Coin::Penny而之后的=>运算符将模式和将要运行的代码分开。这里的代码就仅仅是值1。每一个分支之间使用逗号分隔。

+

每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个match表达式的返回值。

+

如果分支代码较短的话可以不适用大括号,正如列表 6-3 中的每个分支都只是返回一个值。如果想要在分支中运行多行代码,可以使用大括号。例如,如下代码在每次使用Coin::Penny调用时都会打印出“Lucky penny!”,同时仍然返回代码块最后的值,1

+
# enum Coin {
+#    Penny,
+#    Nickel,
+#    Dime,
+#    Quarter,
+# }
+#
+fn value_in_cents(coin: Coin) -> i32 {
+    match coin {
+        Coin::Penny => {
+            println!("Lucky penny!");
+            1
+        },
+        Coin::Nickel => 5,
+        Coin::Dime => 10,
+        Coin::Quarter => 25,
+    }
+}
+
+

绑定值的模式

+

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

+

作为一个例子,让我们修改枚举的一个成员来存放数据。1999 年到 2008 年间,美帝在 25 美分的硬币的一侧为 50 个州每一个都印刷了不同的设计。其他的硬币都没有这种区分州的设计,所以只有这些 25 美分硬币有特殊的价值。可以将这些信息加入我们的enum,通过改变Quarter成员来包含一个State值,列表 6-4 中完成了这些修改:

+
+
#[derive(Debug)] // So we can inspect the state in a minute
+enum UsState {
+    Alabama,
+    Alaska,
+    // ... etc
+}
+
+enum Coin {
+    Penny,
+    Nickel,
+    Dime,
+    Quarter(UsState),
+}
+
+
+

Listing 6-4: A Coin enum where the Quarter variant also holds a UsState +value

+
+
+

想象一下我们的一个朋友尝试收集所有 50 个州的 25 美分硬币。在根据硬币类型分类零钱的同时,也可以报告出每个 25 美分硬币所对应的州名称,这样如何我们的朋友没有的话,他可以把它加入收藏。

+

在这些代码的匹配表达式中,我们在匹配Coin::Quarter成员的分支的模式中增加了一个叫做state的变量。当匹配到Coin::Quarter时,变量state将会绑定 25 美分硬币所对应州的值。接着在代码那个分支中使用state,如下:

+
# #[derive(Debug)]
+# enum UsState {
+#    Alabama,
+#    Alaska,
+# }
+#
+# enum Coin {
+#    Penny,
+#    Nickel,
+#    Dime,
+#    Quarter(UsState),
+# }
+#
+fn value_in_cents(coin: Coin) -> i32 {
+    match coin {
+        Coin::Penny => 1,
+        Coin::Nickel => 5,
+        Coin::Dime => 10,
+        Coin::Quarter(state) => {
+            println!("State quarter from {:?}!", state);
+            25
+        },
+    }
+}
+
+

如果调用value_in_cents(Coin::Quarter(UsState::Alaska))coin将是Coin::Quarter(UsState::Alaska)。当将值与每个分支相比较时,没有分支会匹配知道遇到Coin::Quarter(state)。这时,state绑定的将会是值UsState::Alaska。接着就可以在println!表达式中使用这个绑定了,像这样就可以获取Coin枚举的Quarter成员中内部的州的值。

+

匹配Option<T>

+

在之前的部分在使用Option<T>时我们想要从Some中取出其内部的T值;也可以像处理Coin枚举那样使用match处理Option<T>!与其直接比较硬币,我们将比较Option<T>的成员,不过match表达式的工作方式保持不变。

+

比如想要编写一个函数,它获取一个Option<i32>并且如果其中有一个值,将其加一。如果其中没有值,函数应该返回None值并不尝试执行任何操作。

+

编写这个函数非常简单,得益于match,它将看起来像列表 6-5 中这样:

+
+
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);
+
+
+

Listing 6-5: A function that uses a match expression on an Option<i32>

+
+
+

匹配Some(T)

+

更仔细的检查plus_one的第一行操作。当调用plus_one(five)时,plus_one函数体中的x将会是值Some(5)。接着将其与每个分支比较。

+
None => None,
+
+

Some(5)并不匹配模式None,所以继续进行下一个分支。

+
Some(i) => Some(i + 1),
+
+

Some(5)Some(i)匹配吗?为什么不呢!他们是相同的成员。i绑定了Some中包含的值,所以i的值是5。接着匹配分支的代码被执行,所以我们将i的值加一并返回一个含有值6的新Some

+

匹配None

+

接着考虑下列表 6-5 中plus_one的第二个调用,这里xNone。我们进入match并与第一个分支相比较。

+
None => None,
+
+

匹配上了!这里没有值来加一,所以程序结束并返回=>右侧的值None,因为第一个分支就匹配到了,其他的分支将不再比较。

+

match与枚举相结合在很多场景中都是有用的。你会在 Rust 代码中看到很多这样的模式:match一个枚举,绑定其中的值到一个变量,接着根据其值执行代码。这在一开有点复杂,不过一旦习惯了,你将希望所有语言都拥有它!这一直是用户的最爱。

+

匹配是穷尽的

+

match还有另一方面需要讨论。考虑一下plus_one函数的这个版本:

+
fn plus_one(x: Option<i32>) -> Option<i32> {
+    match x {
+        Some(i) => Some(i + 1),
+    }
+}
+
+

我们没有处理None的情况,所以这些代码会造成一个 bug。幸运的是,这是一个 Rust 知道如何处理的 bug。如果尝试编译这段代码,会得到这个错误:

+
error[E0004]: non-exhaustive patterns: `None` not covered
+ -->
+  |
+6 |         match x {
+  |               ^ pattern `None` not covered
+
+

Rust 知道我们没有覆盖所有可能的情况甚至知道那些模式被忘记了!Rust 中的匹配是穷尽的(*exhaustive):必须穷举到最后的可能性来使代码有效。特别的在这个Option<T>的例子中,Rust 防止我们忘记明确的处理None的情况,这使我们免于假设拥有一个实际上为空的值,这造成了之前提到过的价值亿万的错误。

+

_通配符

+

Rust 也提供了一个模式用于不想列举出所有可能值的场景。例如,u8可以拥有 0 到 255 的有效的值,如果我们只关心 1、3、5 和 7 这几个值,就并不想必须列出 0、2、4、6、8、9 一直到 255 的值。所幸我们不必这么做:可以使用特殊的模式_替代:

+
let some_u8_value = 0u8;
+match some_u8_value {
+    1 => println!("one"),
+    3 => println!("three"),
+    5 => println!("five"),
+    7 => println!("seven"),
+    _ => (),
+}
+
+

_模式会匹配所有的值。通过将其放置于其他分支之后,_将会匹配所有之前没有指定的可能的值。()就是 unit 值,所以_的情况什么也不会发生。因此,可以说我们想要对_通配符之前没有列出的所有可能的值不做任何处理。

+

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

+

if let简单控制流

+
+

ch06-03-if-let.md +
+commit 396e2db4f7de2e5e7869b1f8bc905c45c631ad7d

+
+

if let语法让我们以一种不那么冗长的方式结合iflet,来处理匹配一个模式的值而忽略其他的值。考虑列表 6-6 中的程序,它匹配一个Option<u8>值并只希望当值是三时执行代码:

+
+
let some_u8_value = Some(0u8);
+match some_u8_value {
+    Some(3) => println!("three"),
+    _ => (),
+}
+
+
+

Listing 6-6: A match that only cares about executing code when the value is +Some(3)

+
+
diff --git a/src/SUMMARY.md b/src/SUMMARY.md index e33c2cd..5b02b6a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -26,4 +26,4 @@ - [枚举和模式匹配](ch06-00-enums.md) - [定义枚举](ch06-01-defining-an-enum.md) - [`match`控制流运算符](ch06-02-match.md) - - [使用`if let`的具体控制流](ch06-03-if-let.md) \ No newline at end of file + - [`if let`简单控制流](ch06-03-if-let.md) \ No newline at end of file diff --git a/src/ch06-02-match.md b/src/ch06-02-match.md index 12ff43e..d739cbd 100644 --- a/src/ch06-02-match.md +++ b/src/ch06-02-match.md @@ -4,4 +4,232 @@ >
> commit 396e2db4f7de2e5e7869b1f8bc905c45c631ad7d -Rust 有一个叫做`match`的极为强大的控制流运算符, \ No newline at end of file +Rust 有一个叫做`match`的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较并根据匹配的模式执行代码。模式可由字面值、变量、通配符和许多其他内容构成;第十八章会讲到所有不同种类的模式以及他们的作用。`match`的力量来源于模式的表现力以及编译器检查,它确保了所有可能的情况都得到处理。 + +把`match`表达式想象成某种硬币分啦机:硬币滑入有着不同大小孔洞的轨道,每一个硬币都会掉入符合它大小的孔洞。同样地,值也会检查`match`的每一个模式,并且在遇到第一个“符合”的模式时,值会进入相关联的代码块并在执行中被使用。 + +因为刚刚提到了硬币,让我们用他们来作为一个使用`match`的例子!我们可以编写一个函数来获取一个未知的(美国)硬币,并以一种类似验钞机的方式,确定它是何种硬币并返回它的美分值,如列表 6-3 中所示: + +
+ +```rust +enum Coin { + Penny, + Nickel, + Dime, + Quarter, +} + +fn value_in_cents(coin: Coin) -> i32 { + match coin { + Coin::Penny => 1, + Coin::Nickel => 5, + Coin::Dime => 10, + Coin::Quarter => 25, + } +} +``` + +
+ +Listing 6-3: An enum and a `match` expression that has the variants of the enum +as its patterns. + +
+
+ +拆开`value_in_cents`函数中的`match`来看。首先,我们列出`match`关键字后跟一个表达式,在这个例子中是`coin`的值。这看起来非常像`if`使用的表达式,不过这里有一个非常大的区别:对于`if`,表达式必须返回一个布尔值。而这里它可以是任何类型的。例子中的`coin`的类型是列表 6-3 中定义的`Coin`枚举。 + +接下来是`match`的分支。一个分支有两个部分:一个模式和一些代码。第一个分支的模式是值`Coin::Penny`而之后的`=>`运算符将模式和将要运行的代码分开。这里的代码就仅仅是值`1`。每一个分支之间使用逗号分隔。 + +每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个`match`表达式的返回值。 + +如果分支代码较短的话可以不适用大括号,正如列表 6-3 中的每个分支都只是返回一个值。如果想要在分支中运行多行代码,可以使用大括号。例如,如下代码在每次使用`Coin::Penny`调用时都会打印出“Lucky penny!”,同时仍然返回代码块最后的值,`1`: + +```rust +# enum Coin { +# Penny, +# Nickel, +# Dime, +# Quarter, +# } +# +fn value_in_cents(coin: Coin) -> i32 { + match coin { + Coin::Penny => { + println!("Lucky penny!"); + 1 + }, + Coin::Nickel => 5, + Coin::Dime => 10, + Coin::Quarter => 25, + } +} +``` + +### 绑定值的模式 + +匹配分支的另一个有用的功能是可以绑定匹配的模式的部分值。这也就是如何从枚举成员中提取值。 + +作为一个例子,让我们修改枚举的一个成员来存放数据。1999 年到 2008 年间,美帝在 25 美分的硬币的一侧为 50 个州每一个都印刷了不同的设计。其他的硬币都没有这种区分州的设计,所以只有这些 25 美分硬币有特殊的价值。可以将这些信息加入我们的`enum`,通过改变`Quarter`成员来包含一个`State`值,列表 6-4 中完成了这些修改: + +
+ +```rust +#[derive(Debug)] // So we can inspect the state in a minute +enum UsState { + Alabama, + Alaska, + // ... etc +} + +enum Coin { + Penny, + Nickel, + Dime, + Quarter(UsState), +} +``` + +
+ +Listing 6-4: A `Coin` enum where the `Quarter` variant also holds a `UsState` +value + +
+
+ +想象一下我们的一个朋友尝试收集所有 50 个州的 25 美分硬币。在根据硬币类型分类零钱的同时,也可以报告出每个 25 美分硬币所对应的州名称,这样如何我们的朋友没有的话,他可以把它加入收藏。 + +在这些代码的匹配表达式中,我们在匹配`Coin::Quarter`成员的分支的模式中增加了一个叫做`state`的变量。当匹配到`Coin::Quarter`时,变量`state`将会绑定 25 美分硬币所对应州的值。接着在代码那个分支中使用`state`,如下: + +```rust +# #[derive(Debug)] +# enum UsState { +# Alabama, +# Alaska, +# } +# +# enum Coin { +# Penny, +# Nickel, +# Dime, +# Quarter(UsState), +# } +# +fn value_in_cents(coin: Coin) -> i32 { + match coin { + Coin::Penny => 1, + Coin::Nickel => 5, + Coin::Dime => 10, + Coin::Quarter(state) => { + println!("State quarter from {:?}!", state); + 25 + }, + } +} +``` + +如果调用`value_in_cents(Coin::Quarter(UsState::Alaska))`,`coin`将是`Coin::Quarter(UsState::Alaska)`。当将值与每个分支相比较时,没有分支会匹配知道遇到`Coin::Quarter(state)`。这时,`state`绑定的将会是值`UsState::Alaska`。接着就可以在`println!`表达式中使用这个绑定了,像这样就可以获取`Coin`枚举的`Quarter`成员中内部的州的值。 + +### 匹配`Option` + +在之前的部分在使用`Option`时我们想要从`Some`中取出其内部的`T`值;也可以像处理`Coin`枚举那样使用`match`处理`Option`!与其直接比较硬币,我们将比较`Option`的成员,不过`match`表达式的工作方式保持不变。 + +比如想要编写一个函数,它获取一个`Option`并且如果其中有一个值,将其加一。如果其中没有值,函数应该返回`None`值并不尝试执行任何操作。 + +编写这个函数非常简单,得益于`match`,它将看起来像列表 6-5 中这样: + +
+ +```rust +fn plus_one(x: Option) -> Option { + match x { + None => None, + Some(i) => Some(i + 1), + } +} + +let five = Some(5); +let six = plus_one(five); +let none = plus_one(None); +``` + +
+ +Listing 6-5: A function that uses a `match` expression on an `Option` + +
+
+ +#### 匹配`Some(T)` + +更仔细的检查`plus_one`的第一行操作。当调用`plus_one(five)`时,`plus_one`函数体中的`x`将会是值`Some(5)`。接着将其与每个分支比较。 + + +```rust,ignore +None => None, +``` + +值`Some(5)`并不匹配模式`None`,所以继续进行下一个分支。 + +```rust,ignore +Some(i) => Some(i + 1), +``` + +`Some(5)`与`Some(i)`匹配吗?为什么不呢!他们是相同的成员。`i`绑定了`Some`中包含的值,所以`i`的值是`5`。接着匹配分支的代码被执行,所以我们将`i`的值加一并返回一个含有值`6`的新`Some`。 + +#### 匹配`None` + +接着考虑下列表 6-5 中`plus_one`的第二个调用,这里`x`是`None`。我们进入`match`并与第一个分支相比较。 + +```rust,ignore +None => None, +``` + +匹配上了!这里没有值来加一,所以程序结束并返回`=>`右侧的值`None`,因为第一个分支就匹配到了,其他的分支将不再比较。 + +将`match`与枚举相结合在很多场景中都是有用的。你会在 Rust 代码中看到很多这样的模式:`match`一个枚举,绑定其中的值到一个变量,接着根据其值执行代码。这在一开有点复杂,不过一旦习惯了,你将希望所有语言都拥有它!这一直是用户的最爱。 + +### 匹配是穷尽的 + +`match`还有另一方面需要讨论。考虑一下`plus_one`函数的这个版本: + +```rust,ignore +fn plus_one(x: Option) -> Option { + match x { + Some(i) => Some(i + 1), + } +} +``` + +我们没有处理`None`的情况,所以这些代码会造成一个 bug。幸运的是,这是一个 Rust 知道如何处理的 bug。如果尝试编译这段代码,会得到这个错误: + +``` +error[E0004]: non-exhaustive patterns: `None` not covered + --> + | +6 | match x { + | ^ pattern `None` not covered +``` + +Rust 知道我们没有覆盖所有可能的情况甚至知道那些模式被忘记了!Rust 中的匹配是**穷尽的**(*exhaustive):必须穷举到最后的可能性来使代码有效。特别的在这个`Option`的例子中,Rust 防止我们忘记明确的处理`None`的情况,这使我们免于假设拥有一个实际上为空的值,这造成了之前提到过的价值亿万的错误。 + +### `_`通配符 + +Rust 也提供了一个模式用于不想列举出所有可能值的场景。例如,`u8`可以拥有 0 到 255 的有效的值,如果我们只关心 1、3、5 和 7 这几个值,就并不想必须列出 0、2、4、6、8、9 一直到 255 的值。所幸我们不必这么做:可以使用特殊的模式`_`替代: + +```rust +let some_u8_value = 0u8; +match some_u8_value { + 1 => println!("one"), + 3 => println!("three"), + 5 => println!("five"), + 7 => println!("seven"), + _ => (), +} +``` + +`_`模式会匹配所有的值。通过将其放置于其他分支之后,`_`将会匹配所有之前没有指定的可能的值。`()`就是 unit 值,所以`_`的情况什么也不会发生。因此,可以说我们想要对`_`通配符之前没有列出的所有可能的值不做任何处理。 + +然而,`match`在只关心**一个**情况的场景中可能就有点啰嗦了。为此 Rust 提供了`if let`。 \ No newline at end of file diff --git a/src/ch06-03-if-let.md b/src/ch06-03-if-let.md index 1984155..5bbf59d 100644 --- a/src/ch06-03-if-let.md +++ b/src/ch06-03-if-let.md @@ -1 +1,26 @@ -# Concise Control Flow with `if let` +## `if let`简单控制流 + +> [ch06-03-if-let.md](https://github.com/rust-lang/book/blob/master/src/ch06-03-if-let.md) +>
+> commit 396e2db4f7de2e5e7869b1f8bc905c45c631ad7d + +`if let`语法让我们以一种不那么冗长的方式结合`if`和`let`,来处理匹配一个模式的值而忽略其他的值。考虑列表 6-6 中的程序,它匹配一个`Option`值并只希望当值是三时执行代码: + +
+ +```rust +let some_u8_value = Some(0u8); +match some_u8_value { + Some(3) => println!("three"), + _ => (), +} +``` + +
+ +Listing 6-6: A `match` that only cares about executing code when the value is +`Some(3)` + +
+
+