diff --git a/src/ch06-02-match.md b/src/ch06-02-match.md index dd166a5..6a8f2f2 100644 --- a/src/ch06-02-match.md +++ b/src/ch06-02-match.md @@ -2,7 +2,7 @@ > [ch06-02-match.md](https://github.com/rust-lang/book/blob/main/src/ch06-02-match.md) >
-> commit b374e75f1d7b743c84a6bb1ef72579a6588bcb8a +> commit 16a17f936229c4429530b97522ce4ce373f1054e Rust 有一个叫做 `match` 的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行相应代码。模式可由字面值、变量、通配符和许多其他内容构成;第十八章会涉及到所有不同种类的模式以及它们的作用。`match` 的力量来源于模式的表现力以及编译器检查,它确保了所有可能的情况都得到处理。 @@ -189,23 +189,66 @@ error[E0004]: non-exhaustive patterns: `None` not covered | ^ pattern `None` not covered ``` -Rust 知道我们没有覆盖所有可能的情况甚至知道哪些模式被忘记了!Rust 中的匹配是 **穷尽的**(*exhaustive*):必须穷举到最后的可能性来使代码有效。特别的在这个 `Option` 的例子中,Rust 防止我们忘记明确的处理 `None` 的情况,这使我们免于假设拥有一个实际上为空的值,这造成了之前提到过的价值亿万的错误。 +Rust 知道我们没有覆盖所有可能的情况甚至知道哪些模式被忘记了!Rust 中的匹配是 **穷尽的**(*exhaustive*):必须穷举到最后的可能性来使代码有效。特别的在这个 `Option` 的例子中,Rust 防止我们忘记明确的处理 `None` 的情况,这让我们免于假设拥有一个实际上为空的值,从而使之前提到的价值亿万的错误不可能发生。 -### `_` 通配符 +### 通配模式和 `_` 占位符 -Rust 也提供了一个模式用于不想列举出所有可能值的场景。例如,`u8` 可以拥有 0 到 255 的有效的值,如果我们只关心 1、3、5 和 7 这几个值,就并不想必须列出 0、2、4、6、8、9 一直到 255 的值。所幸我们不必这么做:可以使用特殊的模式 `_` 替代: +让我们看一个例子,我们希望对一些特定的值采取特殊操作,而对其他的值采取默认操作。想象我们正在玩一个游戏,如果你掷出骰子的值为 3,角色不会移动,而是会得到一顶新奇的帽子。如果你掷出了 7,你的角色将失去新奇的帽子。对于其他的数值,你的角色会在棋盘上移动相应的格子。这是一个实现了上述逻辑的 `match`,骰子的结果是硬编码而不是一个随机值,其他的逻辑部分使用了没有函数体的函数来表示,实现它们超出了本例的范围: ```rust -let some_u8_value = 0u8; -match some_u8_value { - 1 => println!("one"), - 3 => println!("three"), - 5 => println!("five"), - 7 => println!("seven"), +let dice_roll = 9; +match dice_roll { + 3 => add_fancy_hat(), + 7 => remove_fancy_hat(), + other => move_player(other), +} + +fn add_fancy_hat() {} +fn remove_fancy_hat() {} +fn move_player(num_spaces: u8) {} +``` + +对于前两个分支,匹配模式是字面值 3 和 7,最后一个分支则涵盖了所有其他可能的值,模式是我们命名为 `other` 的一个变量。`other` 分支的代码通过将其传递给 `move_player` 函数来使用这个变量。 + +即使我们没有列出 `u8` 所有可能的值,这段代码依然能够编译,因为最后一个模式将匹配所有未被特殊列出的值。这种通配模式满足了 `match` 必须被穷尽的要求。请注意,我们必须将通配分支放在最后,因为模式是按顺序匹配的。如果我们在通配分支后添加其他分支,Rust 将会警告我们,因为此后的分支永远不会被匹配到。 + +Rust 还提供了一个模式,当我们不想使用通配模式获取的值时,请使用 `_` ,这是一个特殊的模式,可以匹配任意值而不绑定到该值。这告诉 Rust 我们不会使用这个值,所以 Rust 也不会警告我们存在未使用的变量。 + +让我们改变游戏规则,当你掷出的值不是 3 或 7 的时候,你必须再次掷出。这种情况下我们不需要使用这个值,所以我们改动代码使用 `_` 来替代变量 `other` : + +```rust +let dice_roll = 9; +match dice_roll { + 3 => add_fancy_hat(), + 7 => remove_fancy_hat(), + _ => reroll(), +} + +fn add_fancy_hat() {} +fn remove_fancy_hat() {} +fn reroll() {} +``` + +这个例子也满足穷举性要求,因为我们在最后一个分支中明确地忽略了其他的值。我们没有忘记处理任何东西。 + +让我们再次改变游戏规则,如果你掷出 3 或 7 以外的值,你的回合将无事发生。我们可以使用单元值(在[“元组类型”][tuples]一节中提到的空元组)作为 `_` 分支的代码: + +[tuples]: ch03-02-data-types.html#元组类型 + +```rust +let dice_roll = 9; +match dice_roll { + 3 => add_fancy_hat(), + 7 => remove_fancy_hat(), _ => (), } + +fn add_fancy_hat() {} +fn remove_fancy_hat() {} ``` -`_` 模式会匹配所有的值。通过将其放置于其他分支之后,`_` 将会匹配所有之前没有指定的可能的值。`()` 就是 unit 值,所以 `_` 的情况什么也不会发生。因此,可以说我们想要对 `_` 通配符之前没有列出的所有可能的值不做任何处理。 +在这里,我们明确告诉 Rust 我们不会使用与前面模式不匹配的值,并且这种情况下我们不想运行任何代码。 + +我们将在[第 18 章][ch18-00-patterns]中介绍更多关于模式和匹配的内容。现在,让我们继续讨论 `if let` 语法,这在 `match` 表达式有点啰嗦的情况下很有用。 -然而,`match` 在只关心 **一个** 情况的场景中可能就有点啰嗦了。为此 Rust 提供了`if let`。 +[ch18-00-patterns]: ch18-00-patterns.html