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.

567 lines
76 KiB

4 weeks ago
<!DOCTYPE HTML>
<html lang="zh-CN" class="light" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>match 和 if let - 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" href="../../highlight.css">
<link rel="stylesheet" href="../../tomorrow-night.css">
<link rel="stylesheet" href="../../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../../theme/style.css">
</head>
<body class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "../../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var 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>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('light')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var 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';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item affix "><a href="../../about-book.html">关于本书</a></li><li class="chapter-item affix "><a href="../../into-rust.html">进入 Rust 编程世界</a></li><li class="chapter-item affix "><a href="../../first-try/sth-you-should-not-do.html">避免从入门到放弃</a></li><li class="chapter-item affix "><a href="../../community.html">社区和锈书</a></li><li class="spacer"></li><li class="chapter-item affix "><a href="../../some-thoughts.html">Xobserve: 一切皆可观测</a></li><li class="chapter-item affix "><a href="../../beat-ai.html">BeatAI: 工程师 AI 入门圣经</a></li><li class="chapter-item affix "><li class="part-title">Rust 语言基础学习</li><li class="spacer"></li><li class="chapter-item "><a href="../../first-try/intro.html"><strong aria-hidden="true">1.</strong> 寻找牛刀,以便小试</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../first-try/installation.html"><strong aria-hidden="true">1.1.</strong> 安装 Rust 环境</a></li><li class="chapter-item "><a href="../../first-try/editor.html"><strong aria-hidden="true">1.2.</strong> 墙推 VSCode!</a></li><li class="chapter-item "><a href="../../first-try/cargo.html"><strong aria-hidden="true">1.3.</strong> 认识 Cargo</a></li><li class="chapter-item "><a href="../../first-try/hello-world.html"><strong aria-hidden="true">1.4.</strong> 不仅仅是 Hello world</a></li><li class="chapter-item "><a href="../../first-try/slowly-downloading.html"><strong aria-hidden="true">1.5.</strong> 下载依赖太慢了?</a></li></ol></li><li class="chapter-item expanded "><a href="../../basic/intro.html"><strong aria-hidden="true">2.</strong> Rust 基础入门</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/variable.html"><strong aria-hidden="true">2.1.</strong> 变量绑定与解构</a></li><li class="chapter-item "><a href="../../basic/base-type/index.html"><strong aria-hidden="true">2.2.</strong> 基本类型</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/base-type/numbers.html"><strong aria-hidden="true">2.2.1.</strong> 数值类型</a></li><li class="chapter-item "><a href="../../basic/base-type/char-bool.html"><strong aria-hidden="true">2.2.2.</strong> 字符、布尔、单元类型</a></li><li class="chapter-item "><a href="../../basic/base-type/statement-expression.html"><strong aria-hidden="true">2.2.3.</strong> 语句与表达式</a></li><li class="chapter-item "><a href="../../basic/base-type/function.html"><strong aria-hidden="true">2.2.4.</strong> 函数</a></li></ol></li><li class="chapter-item "><a href="../../basic/ownership/index.html"><strong aria-hidden="true">2.3.</strong> 所有权和借用</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/ownership/ownership.html"><strong aria-hidden="true">2.3.1.</strong> 所有权</a></li><li class="chapter-item "><a href="../../basic/ownership/borrowing.html"><strong aria-hidden="true">2.3.2.</strong> 引用与借用</a></li></ol></li><li class="chapter-item "><a href="../../basic/compound-type/intro.html"><strong aria-hidden="true">2.4.</strong> 复合类型</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/compound-type/string-slice.html"><strong aria-hidden="true">2.4.1.</strong> 字符串与切片</a></li><li class="chapter-item "><a href="../../basic/compound-type/tuple.html"><strong aria-hidden="true">2.4.2.</strong> 元组</a></li><li class="chapter-item "><a href="../../basic/compound-type/struct.html"><strong aria-hidden="true">2.4.3.</strong> 结构体</a></li><li class="chapter-item "><a href="../../basic/compound-type/enum.html"><strong aria-hidden="true">2.4.4.</strong> 枚举</a></li><li class="chapter-item "><a href="../../basic/compound-type/array.html"><strong aria-hidden="true">2.4.5.</strong><>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<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="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/match-if-let.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">
<!-- Page table of contents -->
<div class="sidetoc"><nav class="pagetoc"></nav></div>
<main>
<h1 id="match-和-if-let"><a class="header" href="#match-和-if-let">match 和 if let</a></h1>
<p>在 Rust 中,模式匹配最常用的就是 <code>match</code><code>if let</code>,本章节将对两者及相关的概念进行详尽介绍。</p>
<p>先来看一个关于 <code>match</code> 的简单例子:</p>
<pre><pre class="playground"><code class="language-rust edition2021">enum Direction {
East,
West,
North,
South,
}
fn main() {
let dire = Direction::South;
match dire {
Direction::East =&gt; println!(&quot;East&quot;),
Direction::North | Direction::South =&gt; {
println!(&quot;South or North&quot;);
},
_ =&gt; println!(&quot;West&quot;),
};
}</code></pre></pre>
<p>这里我们想去匹配 <code>dire</code> 对应的枚举类型,因此在 <code>match</code> 中用三个匹配分支来完全覆盖枚举变量 <code>Direction</code> 的所有成员类型,有以下几点值得注意:</p>
<ul>
<li><code>match</code> 的匹配必须要穷举出所有可能,因此这里用 <code>_</code> 来代表未列出的所有可能性</li>
<li><code>match</code> 的每一个分支都必须是一个表达式,且所有分支的表达式最终返回值的类型必须相同</li>
<li><strong>X | Y</strong>,类似逻辑运算符 <code></code>,代表该分支可以匹配 <code>X</code> 也可以匹配 <code>Y</code>,只要满足一个即可</li>
</ul>
<p>其实 <code>match</code> 跟其他语言中的 <code>switch</code> 非常像,<code>_</code> 类似于 <code>switch</code> 中的 <code>default</code></p>
<h2 id="match-匹配"><a class="header" href="#match-匹配"><code>match</code> 匹配</a></h2>
<p>首先来看看 <code>match</code> 的通用形式:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>match target {
模式1 =&gt; 表达式1,
模式2 =&gt; {
语句1;
语句2;
表达式2
},
_ =&gt; 表达式3
}
<span class="boring">}</span></code></pre></pre>
<p>该形式清晰的说明了何为模式,何为模式匹配:将模式与 <code>target</code> 进行匹配,即为模式匹配,而模式匹配不仅仅局限于 <code>match</code>,后面我们会详细阐述。</p>
<p><code>match</code> 允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行对应的代码,下面让我们来一一详解,先看一个例子:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -&gt; u8 {
match coin {
Coin::Penny =&gt; {
println!(&quot;Lucky penny!&quot;);
1
},
Coin::Nickel =&gt; 5,
Coin::Dime =&gt; 10,
Coin::Quarter =&gt; 25,
}
}
<span class="boring">}</span></code></pre></pre>
<p><code>value_in_cents</code> 函数根据匹配到的硬币,返回对应的美分数值。<code>match</code> 后紧跟着的是一个表达式,跟 <code>if</code> 很像,但是 <code>if</code> 后的表达式必须是一个布尔值,而 <code>match</code> 后的表达式返回值可以是任意类型,只要能跟后面的分支中的模式匹配起来即可,这里的 <code>coin</code> 是枚举 <code>Coin</code> 类型。</p>
<p>接下来是 <code>match</code> 的分支。一个分支有两个部分:<strong>一个模式和针对该模式的处理代码</strong>。第一个分支的模式是 <code>Coin::Penny</code>,其后的 <code>=&gt;</code> 运算符将模式和将要运行的代码分开。这里的代码就仅仅是表达式 <code>1</code>,不同分支之间使用逗号分隔。</p>
<p><code>match</code> 表达式执行时,它将目标值 <code>coin</code> 按顺序依次与每一个分支的模式相比较,如果模式匹配了这个值,那么模式之后的代码将被执行。如果模式并不匹配这个值,将继续执行下一个分支。</p>
<p>每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个 <code>match</code> 表达式的返回值。如果分支有多行代码,那么需要用 <code>{}</code> 包裹,同时最后一行代码需要是一个表达式。</p>
<h4 id="使用-match-表达式赋值"><a class="header" href="#使用-match-表达式赋值">使用 <code>match</code> 表达式赋值</a></h4>
<p>还有一点很重要,<code>match</code> 本身也是一个表达式,因此可以用它来赋值:</p>
<pre><pre class="playground"><code class="language-rust edition2021">enum IpAddr {
Ipv4,
Ipv6
}
fn main() {
let ip1 = IpAddr::Ipv6;
let ip_str = match ip1 {
IpAddr::Ipv4 =&gt; &quot;127.0.0.1&quot;,
_ =&gt; &quot;::1&quot;,
};
println!(&quot;{}&quot;, ip_str);
}</code></pre></pre>
<p>因为这里匹配到 <code>_</code> 分支,所以将 <code>&quot;::1&quot;</code> 赋值给了 <code>ip_str</code></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>#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
// --snip--
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState), // 25美分硬币
}
<span class="boring">}</span></code></pre></pre>
<p>其中 <code>Coin::Quarter</code> 成员还存放了一个值:美国的某个州(因为在 1999 年到 2008 年间,美国在 25 美分(Quarter)硬币的背后为 50 个州印刷了不同的标记,其它硬币都没有这样的设计)。</p>
<p>接下来,我们希望在模式匹配中,获取到 25 美分硬币上刻印的州的名称:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn value_in_cents(coin: Coin) -&gt; u8 {
match coin {
Coin::Penny =&gt; 1,
Coin::Nickel =&gt; 5,
Coin::Dime =&gt; 10,
Coin::Quarter(state) =&gt; {
println!(&quot;State quarter from {:?}!&quot;, state);
25
},
}
}
<span class="boring">}</span></code></pre></pre>
<p>上面代码中,在匹配 <code>Coin::Quarter(state)</code> 模式时,我们把它内部存储的值绑定到了 <code>state</code> 变量上,因此 <code>state</code> 变量就是对应的 <code>UsState</code> 枚举类型。</p>
<p>例如有一个印了阿拉斯加州标记的 25 分硬币:<code>Coin::Quarter(UsState::Alaska)</code>,它在匹配时,<code>state</code> 变量将被绑定 <code>UsState::Alaska</code> 的枚举值。</p>
<p>再来看一个更复杂的例子:</p>
<pre><pre class="playground"><code class="language-rust edition2021">enum Action {
Say(String),
MoveTo(i32, i32),
ChangeColorRGB(u16, u16, u16),
}
fn main() {
let actions = [
Action::Say(&quot;Hello Rust&quot;.to_string()),
Action::MoveTo(1,2),
Action::ChangeColorRGB(255,255,0),
];
for action in actions {
match action {
Action::Say(s) =&gt; {
println!(&quot;{}&quot;, s);
},
Action::MoveTo(x, y) =&gt; {
println!(&quot;point from (0, 0) move to ({}, {})&quot;, x, y);
},
Action::ChangeColorRGB(r, g, _) =&gt; {
println!(&quot;change color into '(r:{}, g:{}, b:0)', 'b' has been ignored&quot;,
r, g,
);
}
}
}
}</code></pre></pre>
<p>运行后输出:</p>
<pre><code class="language-console">$ cargo run
Compiling world_hello v0.1.0 (/Users/sunfei/development/rust/world_hello)
Finished dev [unoptimized + debuginfo] target(s) in 0.16s
Running `target/debug/world_hello`
Hello Rust
point from (0, 0) move to (1, 2)
change color into '(r:255, g:255, b:0)', 'b' has been ignored
</code></pre>
<h4 id="穷尽匹配"><a class="header" href="#穷尽匹配">穷尽匹配</a></h4>
<p>在文章的开头,我们简单总结过 <code>match</code> 的匹配必须穷尽所有情况,下面来举例说明,例如:</p>
<pre><pre class="playground"><code class="language-rust edition2021">enum Direction {
East,
West,
North,
South,
}
fn main() {
let dire = Direction::South;
match dire {
Direction::East =&gt; println!(&quot;East&quot;),
Direction::North | Direction::South =&gt; {
println!(&quot;South or North&quot;);
},
};
}</code></pre></pre>
<p>我们没有处理 <code>Direction::West</code> 的情况,因此会报错:</p>
<pre><code class="language-console">error[E0004]: non-exhaustive patterns: `West` not covered // 非穷尽匹配,`West` 没有被覆盖
--&gt; src/main.rs:10:11
|
1 | / enum Direction {
2 | | East,
3 | | West,
| | ---- not covered
4 | | North,
5 | | South,
6 | | }
| |_- `Direction` defined here
...
10 | match dire {
| ^^^^ pattern `West` not covered // 模式 `West` 没有被覆盖
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `Direction`
</code></pre>
<p>不禁想感叹Rust 的编译器<strong>真强大</strong>忍不住想爆粗口了sorry如果你以后进一步深入使用 Rust 也会像我这样感叹的。Rust 编译器清晰地知道 <code>match</code> 中有哪些分支没有被覆盖,这种行为能强制我们处理所有的可能性,有效避免传说中价值<strong>十亿美金</strong><code>null</code> 陷阱。</p>
<h4 id="_-通配符"><a class="header" href="#_-通配符"><code>_</code> 通配符</a></h4>
<p>当我们不想在匹配时列出所有值的时候,可以使用 Rust 提供的一个特殊<strong>模式</strong>,例如,<code>u8</code> 可以拥有 0 到 255 的有效的值,但是我们只关心 <code>1、3、5 和 7</code> 这几个值,不想列出其它的 <code>0、2、4、6、8、9 一直到 255</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>let some_u8_value = 0u8;
match some_u8_value {
1 =&gt; println!(&quot;one&quot;),
3 =&gt; println!(&quot;three&quot;),
5 =&gt; println!(&quot;five&quot;),
7 =&gt; println!(&quot;seven&quot;),
_ =&gt; (),
}
<span class="boring">}</span></code></pre></pre>
<p>通过将 <code>_</code> 其放置于其他分支后,<code>_</code> 将会匹配所有遗漏的值。<code>()</code> 表示返回<strong>单元类型</strong>与所有分支返回值的类型相同,所以当匹配到 <code>_</code> 后,什么也不会发生。</p>
<p>除了<code>_</code>通配符,用一个变量来承载其他情况也是可以的。</p>
<pre><pre class="playground"><code class="language-rust edition2021">#[derive(Debug)]
enum Direction {
East,
West,
North,
South,
}
fn main() {
let dire = Direction::South;
match dire {
Direction::East =&gt; println!(&quot;East&quot;),
other =&gt; println!(&quot;other direction: {:?}&quot;, other),
};
}</code></pre></pre>
<p>然而,在某些场景下,我们其实只关心<strong>某一个值是否存在</strong>,此时 <code>match</code> 就显得过于啰嗦。</p>
<h2 id="if-let-匹配"><a class="header" href="#if-let-匹配"><code>if let</code> 匹配</a></h2>
<p>有时会遇到只有一个模式的值需要被处理,其它值直接忽略的场景,如果用 <code>match</code> 来处理就要写成下面这样:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span> let v = Some(3u8);
match v {
Some(3) =&gt; println!(&quot;three&quot;),
_ =&gt; (),
}
<span class="boring">}</span></code></pre></pre>
<p>我们只想要对 <code>Some(3)</code> 模式进行匹配, 不想处理任何其他 <code>Some&lt;u8&gt;</code> 值或 <code>None</code> 值。但是为了满足 <code>match</code> 表达式(穷尽性)的要求,写代码时必须在处理完这唯一的成员后加上 <code>_ =&gt; ()</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(3) = v {
println!(&quot;three&quot;);
}
<span class="boring">}</span></code></pre></pre>
<p>这两种匹配对于新手来说,可能有些难以抉择,但是只要记住一点就好:<strong>当你只要匹配一个条件,且忽略其他条件时就用 <code>if let</code> ,否则都用 <code>match</code></strong></p>
<h2 id="matches宏"><a class="header" href="#matches宏">matches!宏</a></h2>
<p>Rust 标准库中提供了一个非常实用的宏:<code>matches!</code>,它可以将一个表达式跟模式进行匹配,然后返回匹配的结果 <code>true</code> or <code>false</code></p>
<p>例如,有一个动态数组,里面存有以下枚举:</p>
<pre><pre class="playground"><code class="language-rust edition2021">enum MyEnum {
Foo,
Bar
}
fn main() {
let v = vec![MyEnum::Foo,MyEnum::Bar,MyEnum::Foo];
}</code></pre></pre>
<p>现在如果想对 <code>v</code> 进行过滤,只保留类型是 <code>MyEnum::Foo</code> 的元素,你可能想这么写:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>v.iter().filter(|x| x == MyEnum::Foo);
<span class="boring">}</span></code></pre></pre>
<p>但是,实际上这行代码会报错,因为你无法将 <code>x</code> 直接跟一个枚举成员进行比较。好在,你可以使用 <code>match</code> 来完成,但是会导致代码更为啰嗦,是否有更简洁的方式?答案是使用 <code>matches!</code></p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>v.iter().filter(|x| matches!(x, MyEnum::Foo));
<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 foo = 'f';
assert!(matches!(foo, 'A'..='Z' | 'a'..='z'));
let bar = Some(4);
assert!(matches!(bar, Some(x) if x &gt; 2));
<span class="boring">}</span></code></pre></pre>
<h2 id="变量遮蔽"><a class="header" href="#变量遮蔽">变量遮蔽</a></h2>
<p>无论是 <code>match</code> 还是 <code>if let</code>,这里都是一个新的代码块,而且这里的绑定相当于新变量,如果你使用同名变量,会发生变量遮蔽:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let age = Some(30);
println!(&quot;在匹配前age是{:?}&quot;,age);
if let Some(age) = age {
println!(&quot;匹配出来的age是{}&quot;,age);
}
println!(&quot;在匹配后age是{:?}&quot;,age);
}</code></pre></pre>
<p><code>cargo run </code>运行后输出如下:</p>
<pre><code class="language-console">在匹配前age是Some(30)
匹配出来的age是30
在匹配后age是Some(30)
</code></pre>
<p>可以看出在 <code>if let</code> 中,<code>=</code> 右边 <code>Some(i32)</code> 类型的 <code>age</code> 被左边 <code>i32</code> 类型的新 <code>age</code> 遮蔽了,该遮蔽一直持续到 <code>if let</code> 语句块的结束。因此第三个 <code>println!</code> 输出的 <code>age</code> 依然是 <code>Some(i32)</code> 类型。</p>
<p>对于 <code>match</code> 类型也是如此:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let age = Some(30);
println!(&quot;在匹配前age是{:?}&quot;,age);
match age {
Some(age) =&gt; println!(&quot;匹配出来的age是{}&quot;,age),
_ =&gt; ()
}
println!(&quot;在匹配后age是{:?}&quot;,age);
}</code></pre></pre>
<p>需要注意的是,<strong><code>match</code> 中的变量遮蔽其实不是那么的容易看出</strong>,因此要小心!其实这里最好不要使用同名,避免难以理解,如下。</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let age = Some(30);
println!(&quot;在匹配前age是{:?}&quot;, age);
match age {
Some(x) =&gt; println!(&quot;匹配出来的age是{}&quot;, x),
_ =&gt; ()
}
println!(&quot;在匹配后age是{:?}&quot;, age);
}</code></pre></pre>
<h2 id="课后练习"><a class="header" href="#课后练习">课后练习</a></h2>
<blockquote>
<p><a href="https://practice-zh.course.rs/pattern-match/match-iflet.html">Rust By Practice</a>,支持代码在线编辑和运行,并提供详细的<a href="https://github.com/sunface/rust-by-practice/blob/master/solutions/pattern-match/match.md">习题解答</a></p>
</blockquote>
<div id="giscus-container"></div>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../../basic/match-pattern/intro.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/option.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/intro.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/option.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>
<script type="text/javascript" charset="utf-8">
var pagePath = "basic/match-pattern/match-if-let.md"
</script>
<!-- Custom JS scripts -->
<script src="../../assets/custom.js"></script>
<script src="../../assets/bigPicture.js"></script>
</div>
</body>
</html>