trpl-zh-cn/docs/ch18-01-all-the-places-for-patterns.html
2017-05-31 23:48:57 +08:00

229 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>所有可能会用到模式的位置 - Rust 程序设计语言 简体中文版</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="Rust 程序设计语言 简体中文版">
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="">
<link rel="stylesheet" href="book.css">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
<link rel="shortcut icon" href="favicon.png">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<!-- MathJax -->
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<!-- Fetch JQuery from CDN but have a local fallback -->
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
if (typeof jQuery == 'undefined') {
document.write(unescape("%3Cscript src='jquery.js'%3E%3C/script%3E"));
}
</script>
</head>
<body class="light">
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme = localStorage.getItem('theme');
if (theme == null) { theme = 'light'; }
$('body').removeClass().addClass(theme);
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var sidebar = localStorage.getItem('sidebar');
if (sidebar === "hidden") { $("html").addClass("sidebar-hidden") }
else if (sidebar === "visible") { $("html").addClass("sidebar-visible") }
</script>
<div id="sidebar" class="sidebar">
<ul class="chapter"><li><a href="ch01-00-introduction.html"><strong>1.</strong> 介绍</a></li><li><ul class="section"><li><a href="ch01-01-installation.html"><strong>1.1.</strong> 安装</a></li><li><a href="ch01-02-hello-world.html"><strong>1.2.</strong> Hello, World!</a></li></ul></li><li><a href="ch02-00-guessing-game-tutorial.html"><strong>2.</strong> 猜猜看教程</a></li><li><a href="ch03-00-common-programming-concepts.html"><strong>3.</strong> 通用编程概念</a></li><li><ul class="section"><li><a href="ch03-01-variables-and-mutability.html"><strong>3.1.</strong> 变量和可变性</a></li><li><a href="ch03-02-data-types.html"><strong>3.2.</strong> 数据类型</a></li><li><a href="ch03-03-how-functions-work.html"><strong>3.3.</strong> 函数如何工作</a></li><li><a href="ch03-04-comments.html"><strong>3.4.</strong> 注释</a></li><li><a href="ch03-05-control-flow.html"><strong>3.5.</strong> 控制流</a></li></ul></li><li><a href="ch04-00-understanding-ownership.html"><strong>4.</strong> 认识所有权</a></li><li><ul class="section"><li><a href="ch04-01-what-is-ownership.html"><strong>4.1.</strong> 什么是所有权</a></li><li><a href="ch04-02-references-and-borrowing.html"><strong>4.2.</strong> 引用 &amp; 借用</a></li><li><a href="ch04-03-slices.html"><strong>4.3.</strong> Slices</a></li></ul></li><li><a href="ch05-00-structs.html"><strong>5.</strong> 结构体</a></li><li><ul class="section"><li><a href="ch05-01-method-syntax.html"><strong>5.1.</strong> 方法语法</a></li></ul></li><li><a href="ch06-00-enums.html"><strong>6.</strong> 枚举和模式匹配</a></li><li><ul class="section"><li><a href="ch06-01-defining-an-enum.html"><strong>6.1.</strong> 定义枚举</a></li><li><a href="ch06-02-match.html"><strong>6.2.</strong> <code>match</code>控制流运算符</a></li><li><a href="ch06-03-if-let.html"><strong>6.3.</strong> <code>if let</code>简单控制流</a></li></ul></li><li><a href="ch07-00-modules.html"><strong>7.</strong> 模块</a></li><li><ul class="section"><li><a href="ch07-01-mod-and-the-filesystem.html"><strong>7.1.</strong> <code>mod</code>和文件系统</a></li><li><a href="ch07-02-controlling-visibility-with-pub.html"><strong>7.2.</strong> 使用<code>pub</code>控制可见性</a></li><li><a href="ch07-03-importing-names-with-use.html"><strong>7.3.</strong> 使用<code>use</code>导入命名</a></li></ul></li><li><a href="ch08-00-common-collections.html"><strong>8.</strong> 通用集合类型</a></li><li><ul class="section"><li><a href="ch08-01-vectors.html"><strong>8.1.</strong> vector</a></li><li><a href="ch08-02-strings.html"><strong>8.2.</strong> 字符串</a></li><li><a href="ch08-03-hash-maps.html"><strong>8.3.</strong> 哈希 map</a></li></ul></li><li><a href="ch09-00-error-handling.html"><strong>9.</strong> 错误处理</a></li><li><ul class="section"><li><a href="ch09-01-unrecoverable-errors-with-panic.html"><strong>9.1.</strong> <code>panic!</code>与不可恢复的错误</a></li><li><a href="ch09-02-recoverable-errors-with-result.html"><strong>9.2.</strong> <code>Result</code>与可恢复的错误</a></li><li><a href="ch09-03-to-panic-or-not-to-panic.html"><strong>9.3.</strong> <code>panic!</code>还是不<code>panic!</code></a></li></ul></li><li><a href="ch10-00-generics.html"><strong>10.</strong> 泛型、trait 和生命周期</a></li><li><ul class="section"><li><a href="ch10-01-syntax.html"><strong>10.1.</strong> 泛型数据类型</a></li><li><a href="ch10-02-traits.html"><strong>10.2.</strong> trait定义共享的行为</a></li><li><a href="ch10-03-lifetime-syntax.html"><strong>10.3.</strong> 生命周期与引用有效性</a></li></ul></li><li><a href="ch11-00-testing.html"><strong>11.</strong> 测试</a></li><li><ul class="section"><li><a href="ch11-01-writing-tests.html"><strong>11.1.</strong> 编写测试</a></li><li><a href="ch11-02-running-tests.html"><strong>11.2.</strong> 运行测试</a></li><li><a href="ch11-03-test-organization.html"><strong>11.3.</strong> 测试的组织结构</a></li></ul></li><li><a href="ch12-00-an-io-project.html"><strong>12.</strong> 一个 I/O 项目</a></li><li><ul class="section"><li><a href="ch12-01-accepting-command-line-arguments.html"><strong>12.1.</strong> 接受命令行参数</a></li><li><a href="ch12-02-reading-a-file.html"><strong>12.2.</strong> 读取文件</a></li><li><a href="ch12-03-improving-error-handling-and-modularity.html"><strong>12.3.</strong> 增强错误处理和模块化</a></li><li><a href="ch12-04-testing-the-librarys-functionality.html"><strong>12.4.</strong> 测试库的功能</a></li><li><a href="ch12-05-working-with-environment-variables.html"><strong>12.5.</strong> 处理环境变量</a></li><li><a href="ch12-06-writing-to-stderr-instead-of-stdout.html"><strong>12.6.</strong> 输出到<code>stderr</code>而不是<code>stdout</code></a></li></ul></li><li><a href="ch13-00-functional-features.html"><strong>13.</strong> Rust 中的函数式语言功能</a></li><li><ul class="section"><li><a href="ch13-01-closures.html"><strong>13.1.</strong> 闭包</a></li><li><a href="ch13-02-iterators.html"><strong>13.2.</strong> 迭代器</a></li><li><a href="ch13-03-improving-our-io-project.html"><strong>13.3.</strong> 改进 I/O 项目</a></li><li><a href="ch13-04-performance.html"><strong>13.4.</strong> 性能</a></li></ul></li><li><a href="ch14-00-more-about-cargo.html"><strong>14.</strong> 更多关于 Cargo 和 Crates.io</a></li><li><ul class="section"><li><a href="ch14-01-release-profiles.html"><strong>14.1.</strong> 发布配置</a></li><li><a href="ch14-02-publishing-to-crates-io.html"><strong>14.2.</strong> 将 crate 发布到 Crates.io</a></li><li><a href="ch14-03-cargo-workspaces.html"><strong>14.3.</strong> Cargo 工作空间</a></li><li><a href="ch14-04-installing-binaries.html"><strong>14.4.</strong> 使用<code>cargo install</code>从 Crates.io 安装文件</a></li><li><a href="ch14-05-extending-cargo.html"><strong>14.5.</strong> Cargo 自定义扩展命令</a></li></ul></li><li><a href="ch15-00-smart-pointers.html"><strong>15.</strong> 智能指针</a></li><li><ul class="section"><li><a href="ch15-01-box.html"><strong>15.1.</strong> <code>Box&lt;T&gt;</code>用于已知大小的堆上数据</a></li><li><a href="ch15-02-deref.html"><strong>15.2.</strong> <code>Deref</code> Trait 允许通过引用访问数据</a></li><li><a href="ch15-03-drop.html"><strong>15.3.</strong> <code>Drop</code> Trait 运行清理代码</a></li><li><a href="ch15-04-rc.html"><strong>15.4.</strong> <code>Rc&lt;T&gt;</code> 引用计数智能指针</a></li><li><a href="ch15-05-interior-mutability.html"><strong>15.5.</strong> <code>RefCell&lt;T&gt;</code>和内部可变性模式</a></li><li><a href="ch15-06-reference-cycles.html"><strong>15.6.</strong> 引用循环和内存泄漏是安全的</a></li></ul></li><li><a href="ch16-00-concurrency.html"><strong>16.</strong> 无畏并发</a></li><li><ul class="section"><li><a href="ch16-01-threads.html"><strong>16.1.</strong> 线程</a></li><li><a href="ch16-02-message-passing.html"><strong>16.2.</strong> 消息传递</a></li><li><a href="ch16-03-shared-state.html"><strong>16.3.</strong> 共享状态</a></li><li><a href="ch16-04-extensible-concurrency-sync-and-send.html"><strong>16.4.</strong> 可扩展的并发:<code>Sync</code><code>Send</code></a></li></ul></li><li><a href="ch17-00-oop.html"><strong>17.</strong> 面向对象</a></li><li><ul class="section"><li><a href="ch17-01-what-is-oo.html"><strong>17.1.</strong> 什么是面向对象?</a></li><li><a href="ch17-02-trait-objects.html"><strong>17.2.</strong> 为使用不同类型的值而设计的 trait 对象</a></li><li><a href="ch17-03-oo-design-patterns.html"><strong>17.3.</strong> 面向对象设计模式的实现</a></li></ul></li><li><a href="ch18-00-patterns.html"><strong>18.</strong> 模式用来匹配值的结构</a></li><li><ul class="section"><li><a href="ch18-01-all-the-places-for-patterns.html" class="active"><strong>18.1.</strong> 所有可能会用到模式的位置</a></li><li><a href="ch18-02-refutability.html"><strong>18.2.</strong> refutable何时模式可能会匹配失败</a></li><li><a href="ch18-03-pattern-syntax.html"><strong>18.3.</strong> 模式的全部语法</a></li></ul></li></ul>
</div>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar" class="menu-bar">
<div class="left-buttons">
<i id="sidebar-toggle" class="fa fa-bars"></i>
<i id="theme-toggle" class="fa fa-paint-brush"></i>
</div>
<h1 class="menu-title">Rust 程序设计语言 简体中文版</h1>
<div class="right-buttons">
<i id="print-button" class="fa fa-print" title="Print this book"></i>
</div>
</div>
<div id="content" class="content">
<a class="header" href="#所有可能会用到模式的位置" name="所有可能会用到模式的位置"><h2>所有可能会用到模式的位置</h2></a>
<blockquote>
<p><a href="https://github.com/rust-lang/book/blob/master/second-edition/src/ch18-01-all-the-places-for-patterns.md">ch18-01-all-the-places-for-patterns.md</a>
<br>
commit 4ca9e513e532a4d229ab5af7dfcc567129623bf4</p>
</blockquote>
<p>模式出现在 Rust 的很多地方。你已经在不经意间使用了很多模式!本部分是一个所有有效模式位置的参考。</p>
<a class="header" href="#match-分支" name="match-分支"><h3><code>match</code> 分支</h3></a>
<p>如第六章所讨论的,一个模式常用的位置是 <code>match</code> 表达式的分支。在形式上 <code>match</code> 表达式由 <code>match</code> 关键字、用于匹配的值和一个或多个分支构成。这些分支包含一个模式和在值匹配分支的模式时运行的表达式:</p>
<pre><code>match VALUE {
PATTERN =&gt; EXPRESSION,
PATTERN =&gt; EXPRESSION,
PATTERN =&gt; EXPRESSION,
}
</code></pre>
<a class="header" href="#穷尽性和默认模式-_" name="穷尽性和默认模式-_"><h4>穷尽性和默认模式 <code>_</code></h4></a>
<p><code>match</code> 表达式必须是穷尽的。当我们把所有分支的模式都放在一起,<code>match</code> 表达式所有可能的值都应该被考虑到。一个确保覆盖每个可能值的方法是在最后一个分支使用捕获所有的模式,比如一个变量名。一个匹配任何值的名称永远也不会失败,因此可以覆盖之前分支模式匹配剩下的情况。</p>
<p>这有一个额外的模式经常被用于结尾的分支:<code>_</code>。它匹配所有情况,不过它从不绑定任何变量。这在例如只希望在某些模式下运行代码而忽略其他值的时候很有用。</p>
<a class="header" href="#if-let-表达式" name="if-let-表达式"><h3><code>if let</code> 表达式</h3></a>
<p>第六章讨论过了 <code>if let</code> 表达式,以及它是如何成为编写等同于只关心一个情况的 <code>match</code> 语句的简写的。<code>if let</code> 可以对应一个可选的 <code>else</code> 和代码在 <code>if let</code> 中的模式不匹配时运行。</p>
<p>列表 18-1 展示了甚至可以组合并匹配 <code>if let</code><code>else if</code><code>else if let</code>。这些代码展示了一系列针对不同条件的检查来决定背景颜色应该是什么。为了达到这个例子的目的,我们创建了硬编码值的变量,在真实程序中则可能由询问用户获得。如果用户指定了中意的颜色,我们将使用它作为背景颜色。如果今天是星期二,背景颜色将是绿色。如果用户指定了他们的年龄字符串并能够成功将其解析为数字的话,我们将根据这个数字使用紫色或者橙色。最后,如果没有一个条件符合,背景颜色将是蓝色:</p>
<p><span class="filename">文件名: src/main.rs</span></p>
<pre><code class="language-rust">fn main() {
let favorite_color: Option&lt;&amp;str&gt; = None;
let is_tuesday = false;
let age: Result&lt;u8, _&gt; = &quot;34&quot;.parse();
if let Some(color) = favorite_color {
println!(&quot;Using your favorite color, {}, as the background&quot;, color);
} else if is_tuesday {
println!(&quot;Tuesday is green day!&quot;);
} else if let Ok(age) = age {
if age &gt; 30 {
println!(&quot;Using purple as the background color&quot;);
} else {
println!(&quot;Using orange as the background color&quot;);
}
} else {
println!(&quot;Using blue as the background color&quot;);
}
}
</code></pre>
<p><span class="caption">列表 18-1: 结合 <code>if let</code><code>else if</code><code>else if let</code><code>else</code></span></p>
<p>这个条件结构允许我们支持复杂的需求。使用这里硬编码的值,例子会打印出 <code>Using purple as the background color</code></p>
<p>注意 <code>if let</code> 也可以像 <code>match</code> 分支那样引入覆盖变量:<code>if let Ok(age) = age</code> 引入了一个新的覆盖变量 <code>age</code>,它包含 <code>Ok</code> 成员中的值。这也意味着 <code>if age &gt; 30</code> 条件需要位于这个代码块内部;不能将两个条件组合为 <code>if let Ok(age) = age &amp;&amp; age &gt; 30</code>,因为我们希望与 30 进行比较的被覆盖的 <code>age</code> 直到大括号开始的新作用域才是有效的。</p>
<p>另外注意这样有很多情况的条件并没有 <code>match</code> 表达式强大,因为其穷尽性没有为编译器所检查。如果去掉最后的 <code>else</code> 块而遗漏处理一些情况,编译器也不会报错。这个例子可能过于复杂以致难以重写为一个可读的 <code>match</code>,所以需要额外注意处理了所有的情况,因为编译器不会为我们检查穷尽性。</p>
<a class="header" href="#while-let" name="while-let"><h3><code>while let</code></h3></a>
<p>一个与 <code>if let</code> 类似的结构体是 <code>while let</code>:它允许只要模式匹配就一直进行 <code>while</code> 循环。列表 18-2 展示了一个使用 <code>while let</code> 的例子,它使用 vector 作为栈并打以先进后出的方式打印出 vector 中的值:</p>
<pre><code class="language-rust">let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!(&quot;{}&quot;, top);
}
</code></pre>
<p><span class="caption">列表 18-2: 使用 <code>while let</code> 循环只要 <code>stack.pop()</code> 返回 <code>Some</code>就打印出其值</span></p>
<p>这个例子会打印出 3、2 和 1。<code>pop</code> 方法取出 vector 的最后一个元素并返回<code>Some(value)</code>,如果 vector 是空的,它返回 <code>None</code><code>while</code> 循环只要 <code>pop</code> 返回 <code>Some</code> 就会一直运行其块中的代码。一旦其返回 <code>None</code><code>while</code>循环停止。我们可以使用 <code>while let</code> 来弹出栈中的每一个元素。</p>
<a class="header" href="#for-循环" name="for-循环"><h3><code>for</code> 循环</h3></a>
<p><code>for</code> 循环,如同第三章所讲的,是 Rust 中最常见的循环结构。那一章所没有讲到的是 <code>for</code> 可以获取一个模式。列表 18-3 中展示了如何使用 <code>for</code> 循环来解构一个元组。<code>enumerate</code> 方法适配一个迭代器来产生元组,其包含值和值的索引:</p>
<pre><code class="language-rust">let v = vec![1, 2, 3];
for (index, value) in v.iter().enumerate() {
println!(&quot;{} is at index {}&quot;, value, index);
}
</code></pre>
<p><span class="caption">列表 18-3: 在 <code>for</code> 循环中使用模式来解构 <code>enumerate</code> 返回的元组</span></p>
<p>这会打印出:</p>
<pre><code>1 is at index 0
2 is at index 1
3 is at index 2
</code></pre>
<p>第一个 <code>enumerate</code> 调用会产生元组 <code>(0, 1)</code>。当这个匹配模式 <code>(index, value)</code><code>index</code> 将会是 0 而 <code>value</code> 将会是 1。</p>
<a class="header" href="#let-语句" name="let-语句"><h3><code>let</code> 语句</h3></a>
<p><code>match</code><code>if let</code> 都是本书之前明确讨论过的使用模式的位置,不过他们不是仅有的<strong>使用过</strong>模式的地方。例如,考虑一下这个直白的 <code>let</code> 变量赋值:</p>
<pre><code class="language-rust">let x = 5;
</code></pre>
<p>本书进行了不下百次这样的操作。你可能没有发觉,不过你这正是在使用模式!<code>let</code> 语句更为正式的样子如下:</p>
<pre><code>let PATTERN = EXPRESSION;
</code></pre>
<p>我们见过的像 <code>let x = 5;</code> 这样的语句中变量名位于 <code>PATTERN</code> 位置;变量名不过是形式特别朴素的模式。</p>
<p>通过 <code>let</code>,我们将表达式与模式比较,并为任何找到的名称赋值。所以例如 <code>let x = 5;</code> 的情况,<code>x</code> 是一个模式代表“将匹配到的值绑定到变量 x”。同时因为名称 <code>x</code> 是整个模式,这个模式实际上等于“将任何值绑定到变量 <code>x</code>,不过它是什么”。</p>
<p>为了更清楚的理解 <code>let</code> 的模式匹配的方面,考虑列表 18-4 中使用 <code>let</code> 和模式解构一个元组:</p>
<pre><code class="language-rust">let (x, y, z) = (1, 2, 3);
</code></pre>
<p><span class="caption">列表 18-4: 使用模式解构元组并一次创建三个变量</span></p>
<p>这里有一个元组与模式匹配。Rust 会比较值 <code>(1, 2, 3)</code> 与模式 <code>(x, y, z)</code> 并发现值匹配这个模式。在这个例子中,将会把 <code>1</code> 绑定到 <code>x</code><code>2</code> 绑定到 <code>y</code> <code>3</code> 绑定到 <code>z</code>。你可以将这个元组模式看作是将三个独立的变量模式结合在一起。</p>
<p>在第十六章中我们见过另一个解构元组的例子,列表 16-6 中,那里解构 <code>mpsc::channel()</code> 的返回值为 <code>tx</code>(发送者)和 <code>rx</code>(接收者)。</p>
<a class="header" href="#函数参数" name="函数参数"><h3>函数参数</h3></a>
<p>类似于 <code>let</code>,函数参数也可以是模式。列表 18-5 中的代码声明了一个叫做 <code>foo</code> 的函数,它获取一个 <code>i32</code> 类型的参数 <code>x</code>,这看起来应该很熟悉:</p>
<pre><code class="language-rust">fn foo(x: i32) {
// code goes here
}
</code></pre>
<p><span class="caption">列表 18-5: 在参数中使用模式的函数签名</span></p>
<p><code>x</code> 部分就是一个模式!类似于之前对 <code>let</code> 所做的,可以在函数参数中匹配元组。列表 18-6 展示了如何可以将传递给函数的元组拆分为值:</p>
<p><span class="filename">文件名: src/main.rs</span></p>
<pre><code class="language-rust">fn print_coordinates(&amp;(x, y): &amp;(i32, i32)) {
println!(&quot;Current location: ({}, {})&quot;, x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&amp;point);
}
</code></pre>
<p><span class="caption">列表 18-6: 一个在参数中解构元组的函数</span></p>
<p>这会打印出 <code>Current location: (3, 5)</code>。当传递值 <code>&amp;(3, 5)</code><code>print_coordinates</code> 时,这个值会匹配模式 <code>&amp;(x, y)</code><code>x</code> 得到了值 3<code>y</code>得到了值 5。</p>
<p>因为如第十三章所讲闭包类似于函数,也可以在闭包参数中使用模式。</p>
<p>在这些可以使用模式的位置中的一个区别是,对于 <code>for</code> 循环、<code>let</code> 和函数参数,其模式必须是 <em>irrefutable</em> 的。接下来让我们讨论这个。</p>
</div>
<!-- Mobile navigation buttons -->
<a href="ch18-00-patterns.html" class="mobile-nav-chapters previous">
<i class="fa fa-angle-left"></i>
</a>
<a href="ch18-02-refutability.html" class="mobile-nav-chapters next">
<i class="fa fa-angle-right"></i>
</a>
</div>
<a href="ch18-00-patterns.html" class="nav-chapters previous" title="You can navigate through the chapters using the arrow keys">
<i class="fa fa-angle-left"></i>
</a>
<a href="ch18-02-refutability.html" class="nav-chapters next" title="You can navigate through the chapters using the arrow keys">
<i class="fa fa-angle-right"></i>
</a>
</div>
<!-- Local fallback for Font Awesome -->
<script>
if ($(".fa").css("font-family") !== "FontAwesome") {
$('<link rel="stylesheet" type="text/css" href="_FontAwesome/css/font-awesome.css">').prependTo('head');
}
</script>
<!-- Livereload script (if served using the cli tool) -->
<script src="highlight.js"></script>
<script src="book.js"></script>
</body>
</html>