|  |  | <!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-practice/tests.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>
 | 
						
						
						
							|  |  | <blockquote>
 | 
						
						
						
							|  |  | <p>开始之前,推荐大家先了解下<a href="https://course.rs/test/intro.html">如何在 Rust 中编写测试代码</a>,这块儿内容不复杂,先了解下有利于本章的继续阅读</p>
 | 
						
						
						
							|  |  | </blockquote>
 | 
						
						
						
							|  |  | <p>在之前的章节中,我们完成了对项目结构的重构,并将进入逻辑代码编程的环节,但在此之前,我们需要先编写一些测试代码,也是最近颇为流行的测试驱动开发模式(TDD, Test Driven Development):</p>
 | 
						
						
						
							|  |  | <ol>
 | 
						
						
						
							|  |  | <li>编写一个注定失败的测试,并且失败的原因和你指定的一样</li>
 | 
						
						
						
							|  |  | <li>编写一个成功的测试</li>
 | 
						
						
						
							|  |  | <li>编写你的逻辑代码,直到通过测试</li>
 | 
						
						
						
							|  |  | </ol>
 | 
						
						
						
							|  |  | <p>这三个步骤将在我们的开发过程中不断循环,直到所有的代码都开发完成并成功通过所有测试。</p>
 | 
						
						
						
							|  |  | <h2 id="注定失败的测试用例"><a class="header" href="#注定失败的测试用例">注定失败的测试用例</a></h2>
 | 
						
						
						
							|  |  | <p>既然要添加测试,那之前的 <code>println!</code> 语句将没有大的用处,毕竟 <code>println!</code> 存在的目的就是为了让我们看到结果是否正确,而现在测试用例将取而代之。</p>
 | 
						
						
						
							|  |  | <p>接下来,在 <code>lib.rs</code> 文件中,添加 <code>tests</code> 模块和 <code>test</code> 函数:</p>
 | 
						
						
						
							|  |  | <pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
 | 
						
						
						
							|  |  | </span><span class="boring">fn main() {
 | 
						
						
						
							|  |  | </span>#[cfg(test)]
 | 
						
						
						
							|  |  | mod tests {
 | 
						
						
						
							|  |  |     use super::*;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     #[test]
 | 
						
						
						
							|  |  |     fn one_result() {
 | 
						
						
						
							|  |  |         let query = "duct";
 | 
						
						
						
							|  |  |         let contents = "\
 | 
						
						
						
							|  |  | Rust:
 | 
						
						
						
							|  |  | safe, fast, productive.
 | 
						
						
						
							|  |  | Pick three.";
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         assert_eq!(vec!["safe, fast, productive."], search(query, contents));
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | <span class="boring">}</span></code></pre></pre>
 | 
						
						
						
							|  |  | <p>测试用例将在指定的内容中搜索 <code>duct</code> 字符串,目测可得:其中有一行内容是包含有目标字符串的。</p>
 | 
						
						
						
							|  |  | <p>但目前为止,还无法运行该测试用例,更何况还想幸灾乐祸的看其失败,原因是 <code>search</code> 函数还没有实现!毕竟是测试驱动、测试先行。</p>
 | 
						
						
						
							|  |  | <pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
 | 
						
						
						
							|  |  | </span><span class="boring">fn main() {
 | 
						
						
						
							|  |  | </span>// in lib.rs
 | 
						
						
						
							|  |  | pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
 | 
						
						
						
							|  |  |     vec![]
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | <span class="boring">}</span></code></pre></pre>
 | 
						
						
						
							|  |  | <p>先添加一个简单的 <code>search</code> 函数实现,非常简单粗暴的返回一个空的数组,显而易见测试用例将成功通过,真是一个居心叵测的测试用例!</p>
 | 
						
						
						
							|  |  | <p>注意这里生命周期 <code>'a</code> 的使用,之前的章节有<a href="https://course.rs/basic/lifetime.html#%E5%87%BD%E6%95%B0%E7%AD%BE%E5%90%8D%E4%B8%AD%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E6%A0%87%E6%B3%A8">详细介绍</a>,不太明白的同学可以回头看看。</p>
 | 
						
						
						
							|  |  | <p>喔,这么复杂的代码,都用上生命周期了!嘚瑟两下试试:</p>
 | 
						
						
						
							|  |  | <pre><code class="language-shell">$ cargo test
 | 
						
						
						
							|  |  |    Compiling minigrep v0.1.0 (file:///projects/minigrep)
 | 
						
						
						
							|  |  |     Finished test [unoptimized + debuginfo] target(s) in 0.97s
 | 
						
						
						
							|  |  |      Running unittests src/lib.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | running 1 test
 | 
						
						
						
							|  |  | test tests::one_result ... FAILED
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | failures:
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | ---- tests::one_result stdout ----
 | 
						
						
						
							|  |  | thread 'main' panicked at 'assertion failed: `(left == right)`
 | 
						
						
						
							|  |  |   left: `["safe, fast, productive."]`,
 | 
						
						
						
							|  |  |  right: `[]`', src/lib.rs:44:9
 | 
						
						
						
							|  |  | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | failures:
 | 
						
						
						
							|  |  |     tests::one_result
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | error: test failed, to rerun pass `--lib`
 | 
						
						
						
							|  |  | </code></pre>
 | 
						
						
						
							|  |  | <p>太棒了!它失败了...</p>
 | 
						
						
						
							|  |  | <h2 id="务必成功的测试用例"><a class="header" href="#务必成功的测试用例">务必成功的测试用例</a></h2>
 | 
						
						
						
							|  |  | <p>接着就是测试驱动的第二步:编写注定成功的测试。当然,前提条件是实现我们的 <code>search</code> 函数。它包含以下步骤:</p>
 | 
						
						
						
							|  |  | <ul>
 | 
						
						
						
							|  |  | <li>遍历迭代 <code>contents</code> 的每一行</li>
 | 
						
						
						
							|  |  | <li>检查该行内容是否包含我们的目标字符串</li>
 | 
						
						
						
							|  |  | <li>若包含,则放入返回值列表中,否则忽略</li>
 | 
						
						
						
							|  |  | <li>返回匹配到的返回值列表</li>
 | 
						
						
						
							|  |  | </ul>
 | 
						
						
						
							|  |  | <h3 id="遍历迭代每一行"><a class="header" href="#遍历迭代每一行">遍历迭代每一行</a></h3>
 | 
						
						
						
							|  |  | <p>Rust 提供了一个很便利的 <code>lines</code> 方法将目标字符串进行按行分割:</p>
 | 
						
						
						
							|  |  | <pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
 | 
						
						
						
							|  |  | </span><span class="boring">fn main() {
 | 
						
						
						
							|  |  | </span>// in lib.rs
 | 
						
						
						
							|  |  | pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
 | 
						
						
						
							|  |  |     for line in contents.lines() {
 | 
						
						
						
							|  |  |         // do something with line
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | <span class="boring">}</span></code></pre></pre>
 | 
						
						
						
							|  |  | <p>这里的 <code>lines</code> 返回一个<a href="https://course.rs/advance/functional-programing/iterator.html">迭代器</a>,关于迭代器在后续章节会详细讲解,现在只要知道 <code>for</code> 可以遍历取出迭代器中的值即可。</p>
 | 
						
						
						
							|  |  | <h3 id="在每一行中查询目标字符串"><a class="header" href="#在每一行中查询目标字符串">在每一行中查询目标字符串</a></h3>
 | 
						
						
						
							|  |  | <pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
 | 
						
						
						
							|  |  | </span><span class="boring">fn main() {
 | 
						
						
						
							|  |  | </span>// in lib.rs
 | 
						
						
						
							|  |  | pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
 | 
						
						
						
							|  |  |     for line in contents.lines() {
 | 
						
						
						
							|  |  |         if line.contains(query) {
 | 
						
						
						
							|  |  |             // do something with line
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | <span class="boring">}</span></code></pre></pre>
 | 
						
						
						
							|  |  | <p>与之前的 <code>lines</code> 函数类似,Rust 的字符串还提供了 <code>contains</code> 方法,用于检查 <code>line</code> 是否包含待查询的 <code>query</code>。</p>
 | 
						
						
						
							|  |  | <p>接下来,只要返回合适的值,就可以完成 <code>search</code> 函数的编写。</p>
 | 
						
						
						
							|  |  | <h3 id="存储匹配到的结果"><a class="header" href="#存储匹配到的结果">存储匹配到的结果</a></h3>
 | 
						
						
						
							|  |  | <p>简单,创建一个 <code>Vec</code> 动态数组,然后将查询到的每一个 <code>line</code> 推进数组中即可:</p>
 | 
						
						
						
							|  |  | <pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
 | 
						
						
						
							|  |  | </span><span class="boring">fn main() {
 | 
						
						
						
							|  |  | </span>// in lib.rs
 | 
						
						
						
							|  |  | pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
 | 
						
						
						
							|  |  |     let mut results = Vec::new();
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     for line in contents.lines() {
 | 
						
						
						
							|  |  |         if line.contains(query) {
 | 
						
						
						
							|  |  |             results.push(line);
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     results
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | <span class="boring">}</span></code></pre></pre>
 | 
						
						
						
							|  |  | <p>至此,<code>search</code> 函数已经完成了既定目标,为了检查功能是否正确,运行下我们之前编写的测试用例:</p>
 | 
						
						
						
							|  |  | <pre><code class="language-shell">$ cargo test
 | 
						
						
						
							|  |  |    Compiling minigrep v0.1.0 (file:///projects/minigrep)
 | 
						
						
						
							|  |  |     Finished test [unoptimized + debuginfo] target(s) in 1.22s
 | 
						
						
						
							|  |  |      Running unittests src/lib.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | running 1 test
 | 
						
						
						
							|  |  | test tests::one_result ... ok
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |      Running unittests src/main.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | running 0 tests
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |    Doc-tests minigrep
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | running 0 tests
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 | 
						
						
						
							|  |  | </code></pre>
 | 
						
						
						
							|  |  | <p>测试通过,意味着我们的代码也完美运行,接下来就是在 <code>run</code> 函数中大显身手了。</p>
 | 
						
						
						
							|  |  | <h3 id="在-run-函数中调用-search-函数"><a class="header" href="#在-run-函数中调用-search-函数">在 run 函数中调用 search 函数</a></h3>
 | 
						
						
						
							|  |  | <pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
 | 
						
						
						
							|  |  | </span><span class="boring">fn main() {
 | 
						
						
						
							|  |  | </span>// in src/lib.rs
 | 
						
						
						
							|  |  | pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
 | 
						
						
						
							|  |  |     let contents = fs::read_to_string(config.file_path)?;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     for line in search(&config.query, &contents) {
 | 
						
						
						
							|  |  |         println!("{line}");
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     Ok(())
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | <span class="boring">}</span></code></pre></pre>
 | 
						
						
						
							|  |  | <p>好,再运行下看看结果,看起来我们距离成功从未如此之近!</p>
 | 
						
						
						
							|  |  | <pre><code class="language-shell">$ cargo run -- frog poem.txt
 | 
						
						
						
							|  |  |    Compiling minigrep v0.1.0 (file:///projects/minigrep)
 | 
						
						
						
							|  |  |     Finished dev [unoptimized + debuginfo] target(s) in 0.38s
 | 
						
						
						
							|  |  |      Running `target/debug/minigrep frog poem.txt`
 | 
						
						
						
							|  |  | How public, like a frog
 | 
						
						
						
							|  |  | </code></pre>
 | 
						
						
						
							|  |  | <p>酷!成功查询到包含 <code>frog</code> 的行,再来试试 <code>body</code> :</p>
 | 
						
						
						
							|  |  | <pre><code class="language-shell">$ cargo run -- body poem.txt
 | 
						
						
						
							|  |  |    Compiling minigrep v0.1.0 (file:///projects/minigrep)
 | 
						
						
						
							|  |  |     Finished dev [unoptimized + debuginfo] target(s) in 0.0s
 | 
						
						
						
							|  |  |      Running `target/debug/minigrep body poem.txt`
 | 
						
						
						
							|  |  | I'm nobody! Who are you?
 | 
						
						
						
							|  |  | Are you nobody, too?
 | 
						
						
						
							|  |  | How dreary to be somebody!
 | 
						
						
						
							|  |  | </code></pre>
 | 
						
						
						
							|  |  | <p>完美,三行,一行不少,为了确保万无一失,再来试试查询一个不存在的单词:</p>
 | 
						
						
						
							|  |  | <pre><code class="language-shell">cargo run -- monomorphization poem.txt
 | 
						
						
						
							|  |  |    Compiling minigrep v0.1.0 (file:///projects/minigrep)
 | 
						
						
						
							|  |  |     Finished dev [unoptimized + debuginfo] target(s) in 0.0s
 | 
						
						
						
							|  |  |      Running `target/debug/minigrep monomorphization poem.txt`
 | 
						
						
						
							|  |  | </code></pre>
 | 
						
						
						
							|  |  | <p>至此,章节开头的目标已经全部完成,接下来思考一个小问题:如果要为程序加上大小写不敏感的控制命令,由用户进行输入,该怎么实现比较好呢?毕竟在实际搜索查询中,同时支持大小写敏感和不敏感还是很重要的。</p>
 | 
						
						
						
							|  |  | <p>答案留待下一章节揭晓。</p>
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |                     </main>
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |                     <nav class="nav-wrapper" aria-label="Page navigation">
 | 
						
						
						
							|  |  |                         <!-- Mobile navigation buttons -->
 | 
						
						
						
							|  |  |                             <a rel="prev" href="../basic-practice/refactoring.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-practice/envs.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-practice/refactoring.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-practice/envs.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>
 |