You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

394 lines
21 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE HTML>
<html lang="zh-CN" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>模式适用场景 - Rust语言圣经(Rust Course)</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../../favicon.svg">
<link rel="shortcut icon" href="../../favicon.png">
<link rel="stylesheet" href="../../css/variables.css">
<link rel="stylesheet" href="../../css/general.css">
<link rel="stylesheet" href="../../css/chrome.css">
<link rel="stylesheet" href="../../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../../theme/style.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust语言圣经(Rust Course)</h1>
<div class="right-buttons">
<a href="../../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/sunface/rust-course" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/sunface/rust-course/edit/main/src/basic/match-pattern/pattern-match.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="模式适用场景"><a class="header" href="#模式适用场景">模式适用场景</a></h1>
<h2 id="模式"><a class="header" href="#模式">模式</a></h2>
<p>模式是 Rust 中的特殊语法,它用来匹配类型中的结构和数据,它往往和 <code>match</code> 表达式联用,以实现强大的模式匹配能力。模式一般由以下内容组合而成:</p>
<ul>
<li>字面值</li>
<li>解构的数组、枚举、结构体或者元组</li>
<li>变量</li>
<li>通配符</li>
<li>占位符</li>
</ul>
<h3 id="所有可能用到模式的地方"><a class="header" href="#所有可能用到模式的地方">所有可能用到模式的地方</a></h3>
<h4 id="match-分支"><a class="header" href="#match-分支">match 分支</a></h4>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>match VALUE {
PATTERN =&gt; EXPRESSION,
PATTERN =&gt; EXPRESSION,
PATTERN =&gt; EXPRESSION,
}
<span class="boring">}</span></code></pre></pre>
<p>如上所示,<code>match</code> 的每个分支就是一个<strong>模式</strong>,因为 <code>match</code> 匹配是穷尽式的,因此我们往往需要一个特殊的模式 <code>_</code>,来匹配剩余的所有情况:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>match VALUE {
PATTERN =&gt; EXPRESSION,
PATTERN =&gt; EXPRESSION,
_ =&gt; EXPRESSION,
}
<span class="boring">}</span></code></pre></pre>
<h4 id="if-let-分支"><a class="header" href="#if-let-分支">if let 分支</a></h4>
<p><code>if let</code> 往往用于匹配一个模式,而忽略剩下的所有模式的场景:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>if let PATTERN = SOME_VALUE {
}
<span class="boring">}</span></code></pre></pre>
<h4 id="while-let-条件循环"><a class="header" href="#while-let-条件循环">while let 条件循环</a></h4>
<p>一个与 <code>if let</code> 类似的结构是 <code>while let</code> 条件循环,它允许只要模式匹配就一直进行 <code>while</code> 循环。下面展示了一个使用 <code>while let</code> 的例子:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Vec是动态数组
let mut stack = Vec::new();
// 向数组尾部插入元素
stack.push(1);
stack.push(2);
stack.push(3);
// stack.pop从数组尾部弹出元素
while let Some(top) = stack.pop() {
println!("{}", top);
}
<span class="boring">}</span></code></pre></pre>
<p>这个例子会打印出 <code>3</code><code>2</code> 接着是 <code>1</code><code>pop</code> 方法取出动态数组的最后一个元素并返回 <code>Some(value)</code>,如果动态数组是空的,将返回 <code>None</code>,对于 <code>while</code> 来说,只要 <code>pop</code> 返回 <code>Some</code> 就会一直不停的循环。一旦其返回 <code>None</code><code>while</code> 循环停止。我们可以使用 <code>while let</code> 来弹出栈中的每一个元素。</p>
<p>你也可以用 <code>loop</code> + <code>if let</code> 或者 <code>match</code> 来实现这个功能,但是会更加啰嗦。</p>
<h4 id="for-循环"><a class="header" href="#for-循环">for 循环</a></h4>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} is at index {}", value, index);
}
<span class="boring">}</span></code></pre></pre>
<p>这里使用 <code>enumerate</code> 方法产生一个迭代器,该迭代器每次迭代会返回一个 <code>(索引,值)</code> 形式的元组,然后用 <code>(index,value)</code> 来匹配。</p>
<h4 id="let-语句"><a class="header" href="#let-语句">let 语句</a></h4>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let PATTERN = EXPRESSION;
<span class="boring">}</span></code></pre></pre>
<p>是的, 该语句我们已经用了无数次了,它也是一种模式匹配:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let x = 5;
<span class="boring">}</span></code></pre></pre>
<p>这其中,<code>x</code> 也是一种模式绑定,代表将<strong>匹配的值绑定到变量 x 上</strong>。因此,在 Rust 中,<strong>变量名也是一种模式</strong>,只不过它比较朴素很不起眼罢了。</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let (x, y, z) = (1, 2, 3);
<span class="boring">}</span></code></pre></pre>
<p>上面将一个元组与模式进行匹配(<strong>模式和值的类型必需相同!</strong>),然后把 <code>1, 2, 3</code> 分别绑定到 <code>x, y, z</code> 上。</p>
<p>模式匹配要求两边的类型必须相同,否则就会导致下面的报错:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let (x, y) = (1, 2, 3);
<span class="boring">}</span></code></pre></pre>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>error[E0308]: mismatched types
--&gt; src/main.rs:4:5
|
4 | let (x, y) = (1, 2, 3);
| ^^^^^^ --------- this expression has type `({integer}, {integer}, {integer})`
| |
| expected a tuple with 3 elements, found one with 2 elements
|
= note: expected tuple `({integer}, {integer}, {integer})`
found tuple `(_, _)`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error
<span class="boring">}</span></code></pre></pre>
<p>对于元组来说,元素个数也是类型的一部分!</p>
<h4 id="函数参数"><a class="header" href="#函数参数">函数参数</a></h4>
<p>函数参数也是模式:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn foo(x: i32) {
// 代码
}
<span class="boring">}</span></code></pre></pre>
<p>其中 <code>x</code> 就是一个模式,你还可以在参数中匹配元组:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn print_coordinates(&amp;(x, y): &amp;(i32, i32)) {
println!("Current location: ({}, {})", x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&amp;point);
}</code></pre></pre>
<p><code>&amp;(3, 5)</code> 会匹配模式 <code>&amp;(x, y)</code>,因此 <code>x</code> 得到了 <code>3</code><code>y</code> 得到了 <code>5</code></p>
<h4 id="let-和-if-let"><a class="header" href="#let-和-if-let">let 和 if let</a></h4>
<p>对于以下代码,编译器会报错:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let Some(x) = some_option_value;
<span class="boring">}</span></code></pre></pre>
<p>因为右边的值可能不为 <code>Some</code>,而是 <code>None</code>,这种时候就不能进行匹配,也就是上面的代码遗漏了 <code>None</code> 的匹配。</p>
<p>类似 <code>let</code> , <code>for</code><code>match</code> 都必须要求完全覆盖匹配,才能通过编译( 不可驳模式匹配 )。</p>
<p>但是对于 <code>if let</code>,就可以这样使用:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>if let Some(x) = some_option_value {
println!("{}", x);
}
<span class="boring">}</span></code></pre></pre>
<p>因为 <code>if let</code> 允许匹配一种模式,而忽略其余的模式( 可驳模式匹配 )。</p>
<h4 id="let-elserust-165-新增"><a class="header" href="#let-elserust-165-新增">let-else(Rust 1.65 新增)</a></h4>
<p>使用 <code>let-else</code> 匹配,即可使 <code>let</code> 变为可驳模式。它可以使用 <code>else</code> 分支来处理模式不匹配的情况,但是 <code>else</code> 分支中必须用发散的代码块处理(例如:<code>break</code><code>return</code><code>panic</code>)。请看下面的代码:</p>
<pre><pre class="playground"><code class="language-rust edition2021">use std::str::FromStr;
fn get_count_item(s: &amp;str) -&gt; (u64, &amp;str) {
let mut it = s.split(' ');
let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
panic!("Can't segment count item pair: '{s}'");
};
let Ok(count) = u64::from_str(count_str) else {
panic!("Can't parse integer: '{count_str}'");
};
// error: `else` clause of `let...else` does not diverge
// let Ok(count) = u64::from_str(count_str) else { 0 };
(count, item)
}
fn main() {
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));
}</code></pre></pre>
<p><code>match</code><code>if let</code> 相比,<code>let-else</code> 的一个显著特点在于其解包成功时所创建的变量具有更广的作用域。在 <code>let-else</code> 语句中,成功匹配后的变量不再仅限于特定分支内使用:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// if let
if let Some(x) = some_option_value {
println!("{}", x);
}
// let-else
let Some(x) = some_option_value else { return; }
println!("{}", x);
<span class="boring">}</span></code></pre></pre>
<p>在上面的例子中,<code>if let</code> 写法里的 <code>x</code> 只能在 <code>if</code> 分支内使用,而 <code>let-else</code> 写法里的 <code>x</code> 则可以在 <code>let</code> 之外使用。</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../../basic/match-pattern/option.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../basic/match-pattern/all-patterns.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../../basic/match-pattern/option.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../basic/match-pattern/all-patterns.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../../ace.js"></script>
<script src="../../editor.js"></script>
<script src="../../mode-rust.js"></script>
<script src="../../theme-dawn.js"></script>
<script src="../../theme-tomorrow_night.js"></script>
<script src="../../elasticlunr.min.js"></script>
<script src="../../mark.min.js"></script>
<script src="../../searcher.js"></script>
<script src="../../clipboard.min.js"></script>
<script src="../../highlight.js"></script>
<script src="../../book.js"></script>
<!-- Custom JS scripts -->
<script src="../../assets/custom2.js"></script>
<script src="../../assets/bigPicture.js"></script>
</div>
</body>
</html>