Rust入门教程(九).html 119 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. <!DOCTYPE html>
  2. <html lang="en-US">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width,initial-scale=1">
  6. <title>Rust入门教程(九):实战演练:一个简单的 grep 程序 | Rust训练营教程文档</title>
  7. <meta name="generator" content="VuePress 1.9.10">
  8. <link rel="icon" href="/rust_camp_tutorial/logo.png">
  9. <meta name="description" content="DragonOS-Rust camp">
  10. <link rel="preload" href="/rust_camp_tutorial/assets/css/0.styles.7dd9be3e.css" as="style"><link rel="preload" href="/rust_camp_tutorial/assets/js/app.d7ab8f65.js" as="script"><link rel="preload" href="/rust_camp_tutorial/assets/js/2.3dc1b8de.js" as="script"><link rel="preload" href="/rust_camp_tutorial/assets/js/1.7f771cfb.js" as="script"><link rel="preload" href="/rust_camp_tutorial/assets/js/27.928f1e6b.js" as="script"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/10.23a1f579.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/11.c389195a.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/12.1d996921.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/13.4d4410c4.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/14.37ef2a72.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/15.5542c093.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/16.d48fd1ce.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/17.bd8d538c.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/18.6d3b94c1.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/19.eb35cfee.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/20.c11ec329.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/21.db1b5d88.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/22.7714be7b.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/23.7f3a9620.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/24.2f88b37d.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/25.df99dd5f.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/26.606cfbc8.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/28.f61a69ee.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/29.802642cf.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/3.5322f14a.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/30.72f41aed.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/31.e1aa8cbc.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/32.30c1ff3b.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/33.f21667a4.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/4.84e1e480.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/5.f0541060.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/6.dfb06aa0.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/7.7551a9fb.js"><link rel="prefetch" href="/rust_camp_tutorial/assets/js/vendors~docsearch.5e19b665.js">
  11. <link rel="stylesheet" href="/rust_camp_tutorial/assets/css/0.styles.7dd9be3e.css">
  12. </head>
  13. <body>
  14. <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/rust_camp_tutorial/" class="home-link router-link-active"><img src="logo.png" alt="Rust训练营教程文档" class="logo"> <span class="site-name can-hide">Rust训练营教程文档</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/rust_camp_tutorial/" class="nav-link">
  15. 首页
  16. </a></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/rust_camp_tutorial/" class="nav-link">
  17. 首页
  18. </a></div> <!----></nav> <ul class="sidebar-links"><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>Rust入门教程(九):实战演练:一个简单的 grep 程序</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#一、接收命令行参数" class="sidebar-link">一、接收命令行参数</a><ul class="sidebar-sub-headers"></ul></li><li><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#二、读取文件" class="sidebar-link">二、读取文件</a><ul class="sidebar-sub-headers"></ul></li><li><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#三、重构-改进模块和错误处理" class="sidebar-link">三、重构:改进模块和错误处理</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#_3-1-四个问题提炼" class="sidebar-link">3.1 四个问题提炼</a></li><li class="sidebar-sub-header"><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#_3-2-二进制程序关注点分离的指导性原则" class="sidebar-link">3.2 二进制程序关注点分离的指导性原则</a></li></ul></li><li><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#四、使用-tdd-测试驱动开发-开发库功能" class="sidebar-link">四、使用 TDD(测试驱动开发)开发库功能</a><ul class="sidebar-sub-headers"></ul></li><li><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#五、使用环境变量" class="sidebar-link">五、使用环境变量</a><ul class="sidebar-sub-headers"></ul></li><li><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#六、将错误消息写进标准错误而不是标准输出" class="sidebar-link">六、将错误消息写进标准错误而不是标准输出</a><ul class="sidebar-sub-headers"></ul></li><li><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#七、完整代码" class="sidebar-link">七、完整代码</a><ul class="sidebar-sub-headers"></ul></li><li><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#八、案例-代码统计" class="sidebar-link">八、案例:代码统计</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#_8-1-基本功能介绍" class="sidebar-link">8.1 基本功能介绍</a></li><li class="sidebar-sub-header"><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#_8-2-可拓展功能" class="sidebar-link">8.2 可拓展功能</a></li><li class="sidebar-sub-header"><a href="/rust_camp_tutorial/Rust%E6%96%87%E6%A1%A3/Rust%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%EF%BC%88%E4%B9%9D%EF%BC%89.html#_8-3-代码仓库及说明" class="sidebar-link">8.3 代码仓库及说明</a></li></ul></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="rust入门教程-九-实战演练-一个简单的-grep-程序"><a href="#rust入门教程-九-实战演练-一个简单的-grep-程序" class="header-anchor">#</a> Rust入门教程(九):实战演练:一个简单的 grep 程序</h1> <blockquote><p>本章将介绍 Rust 在实际开发中的使用,并用官方文档上的项目(一个简单版本的 grep 命令)展开讲解,最后将利用之前所学,自己实现一个代码统计的 Rust 小项目。
  19. 要实现的 grep 命令功能很简单,就是在指定文件中查找指定文字。grep 命令接收一个文件名和字符串作为输入参数,然后读取文件内容,搜索包含指定字符串的行,最终将这些匹配的行打印输出。</p></blockquote> <p>下面开始实战演示。</p> <h2 id="一、接收命令行参数"><a href="#一、接收命令行参数" class="header-anchor">#</a> 一、接收命令行参数</h2> <p>我们预计使用如下命令来执行该程序:</p> <div class="language-bash extra-class"><pre class="language-bash"><code><span class="token function">cargo</span> run <span class="token operator">&lt;</span>string<span class="token operator">&gt;</span> <span class="token operator">&lt;</span>filename<span class="token operator">&gt;</span>
  20. </code></pre></div><p>因此我们首先要读取命令行中的参数,我们导入函数 <code>use std::env::args()</code>,<code>args()</code> 函数返回一个迭代器,迭代器部分的内容将在后面才会介绍。然后使用 <code>collect</code> 方法,将迭代器中的值转化成一个集合,但是该函数不能处理命令行中非 <code>Unicode</code> 的字符(这种情况可以使用 <code>env::args _os()</code> 函数,这种情况下返回的迭代器值的类型是 <code>OsString</code>,在这里不做介绍)。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>env<span class="token punctuation">;</span>
  21. <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  22. <span class="token keyword">let</span> args<span class="token punctuation">:</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token class-name">String</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token namespace">env<span class="token punctuation">::</span></span><span class="token function">args</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">collect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  23. <span class="token keyword">let</span> search_string <span class="token operator">=</span> <span class="token operator">&amp;</span>args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  24. <span class="token keyword">let</span> filename <span class="token operator">=</span> <span class="token operator">&amp;</span>args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  25. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;{:?}&quot;</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>
  26. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;Search String {}&quot;</span><span class="token punctuation">,</span> search_string<span class="token punctuation">)</span><span class="token punctuation">;</span>
  27. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;In file {}&quot;</span><span class="token punctuation">,</span> filename<span class="token punctuation">)</span><span class="token punctuation">;</span>
  28. <span class="token punctuation">}</span>
  29. </code></pre></div><div class="language-rust extra-class"><pre class="language-rust"><code>➜ ~<span class="token operator">/</span>code<span class="token operator">/</span>rust<span class="token operator">/</span>minigrep git<span class="token punctuation">:</span><span class="token punctuation">(</span>master<span class="token punctuation">)</span> ✗ cargo run string filename
  30. <span class="token class-name">Compiling</span> minigrep v0<span class="token number">.1</span><span class="token punctuation">.</span><span class="token number">0</span> <span class="token punctuation">(</span><span class="token operator">/</span>home<span class="token operator">/</span>cherry<span class="token operator">/</span>code<span class="token operator">/</span>rust<span class="token operator">/</span>minigrep<span class="token punctuation">)</span>
  31. <span class="token class-name">Finished</span> dev <span class="token punctuation">[</span>unoptimized <span class="token operator">+</span> debuginfo<span class="token punctuation">]</span> <span class="token function">target</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token number">0</span><span class="token punctuation">.</span>29s
  32. <span class="token class-name">Running</span> `target<span class="token operator">/</span>debug<span class="token operator">/</span>minigrep string filename`
  33. <span class="token punctuation">[</span><span class="token string">&quot;target/debug/minigrep&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;string&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;filename&quot;</span><span class="token punctuation">]</span>
  34. <span class="token class-name">Search</span> <span class="token class-name">String</span> string
  35. <span class="token class-name">In</span> file filename
  36. </code></pre></div><p>根据程序执行结果我们能够得知:返回的第一个参数永远都是该程序的二进制文件(对应 <code>args[0]</code>),从第二个参数开始才是从命令行输入的各种参数(对应 <code>args[1]</code> ...)。</p> <h2 id="二、读取文件"><a href="#二、读取文件" class="header-anchor">#</a> 二、读取文件</h2> <p>首先导入模块 <code>use std::fs</code>,用于处理和文件相关的事务,<code>read_to_string()</code> 用来读取文件中的内容,将其转化成字符串。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>env<span class="token punctuation">;</span>
  37. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>fs<span class="token punctuation">;</span>
  38. <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  39. <span class="token keyword">let</span> args<span class="token punctuation">:</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token class-name">String</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token namespace">env<span class="token punctuation">::</span></span><span class="token function">args</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">collect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  40. <span class="token keyword">let</span> search_string <span class="token operator">=</span> <span class="token operator">&amp;</span>args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  41. <span class="token keyword">let</span> filename <span class="token operator">=</span> <span class="token operator">&amp;</span>args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  42. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;{:?}&quot;</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>
  43. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;Search String {}&quot;</span><span class="token punctuation">,</span> search_string<span class="token punctuation">)</span><span class="token punctuation">;</span>
  44. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;In file {}&quot;</span><span class="token punctuation">,</span> filename<span class="token punctuation">)</span><span class="token punctuation">;</span>
  45. <span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token namespace">fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>filename<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">expect</span><span class="token punctuation">(</span><span class="token string">&quot;该文件不存在&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  46. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;文件内容:\n{}&quot;</span><span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
  47. <span class="token punctuation">}</span>
  48. </code></pre></div><p>输出结果为:</p> <div class="language-rust extra-class"><pre class="language-rust"><code>➜ ~<span class="token operator">/</span>code<span class="token operator">/</span>rust<span class="token operator">/</span>minigrep git<span class="token punctuation">:</span><span class="token punctuation">(</span>master<span class="token punctuation">)</span> ✗ cargo run string poem
  49. <span class="token class-name">Finished</span> dev <span class="token punctuation">[</span>unoptimized <span class="token operator">+</span> debuginfo<span class="token punctuation">]</span> <span class="token function">target</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token number">0</span><span class="token punctuation">.</span>00s
  50. <span class="token class-name">Running</span> `target<span class="token operator">/</span>debug<span class="token operator">/</span>minigrep string poem`
  51. <span class="token punctuation">[</span><span class="token string">&quot;target/debug/minigrep&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;string&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;poem&quot;</span><span class="token punctuation">]</span>
  52. <span class="token class-name">Search</span> <span class="token class-name">String</span> string
  53. <span class="token class-name">In</span> file poem
  54. 文件内容<span class="token punctuation">:</span>
  55. <span class="token class-name">Hold</span> fast to dreams
  56. <span class="token class-name">For</span> <span class="token keyword">if</span> dreams die
  57. <span class="token class-name">Life</span> is a broken<span class="token operator">-</span>winged bird
  58. <span class="token class-name">That</span> can never fly
  59. <span class="token class-name">Hold</span> fast to dreams
  60. <span class="token class-name">For</span> when dreams go
  61. <span class="token class-name">Life</span> is a barren field
  62. <span class="token class-name">Frozen</span> only with snow
  63. <span class="token class-name">To</span> see a world <span class="token keyword">in</span> a grain of sand<span class="token punctuation">,</span>
  64. <span class="token class-name">And</span> a heaven <span class="token keyword">in</span> a wild flower<span class="token punctuation">,</span>
  65. <span class="token class-name">Hold</span> infinity <span class="token keyword">in</span> the palm of your hand<span class="token punctuation">,</span>
  66. <span class="token class-name">And</span> eternity <span class="token keyword">in</span> an hour<span class="token punctuation">.</span>
  67. </code></pre></div><p>当然目前看来所有逻辑都放在了主函数中,并且很多错误情况都没有考虑。一般情况下一个函数只做一件事,如果代码逐渐变多,代码维护将变得越来越困难。代码越少重构越简单,因此下一节将对代码进行重构。</p> <h2 id="三、重构-改进模块和错误处理"><a href="#三、重构-改进模块和错误处理" class="header-anchor">#</a> 三、重构:改进模块和错误处理</h2> <h3 id="_3-1-四个问题提炼"><a href="#_3-1-四个问题提炼" class="header-anchor">#</a> 3.1 四个问题提炼</h3> <p>我们仔细观察一下目前的代码,主要有四个方面的问题。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>env<span class="token punctuation">;</span>
  68. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>fs<span class="token punctuation">;</span>
  69. <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  70. <span class="token keyword">let</span> args<span class="token punctuation">:</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token class-name">String</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token namespace">env<span class="token punctuation">::</span></span><span class="token function">args</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">collect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  71. <span class="token keyword">let</span> search_string <span class="token operator">=</span> <span class="token operator">&amp;</span>args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  72. <span class="token keyword">let</span> filename <span class="token operator">=</span> <span class="token operator">&amp;</span>args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  73. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;{:?}&quot;</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>
  74. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;Search String {}&quot;</span><span class="token punctuation">,</span> search_string<span class="token punctuation">)</span><span class="token punctuation">;</span>
  75. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;In file {}&quot;</span><span class="token punctuation">,</span> filename<span class="token punctuation">)</span><span class="token punctuation">;</span>
  76. <span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token namespace">fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>filename<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">expect</span><span class="token punctuation">(</span><span class="token string">&quot;该文件不存在&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  77. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;文件内容:\n{}&quot;</span><span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
  78. <span class="token punctuation">}</span>
  79. </code></pre></div><ol><li>主函数负责的功能较多,既要负责命令行参数解析,又要负责读取文件。而程序编写的原则就是一个函数负责一个功能,因此要将主函数拆分;</li> <li><code>search_string</code>、<code>filename</code> 和 <code>content</code> 变量,在程序越来越大之后,变量也会越来越多,将难以追踪每一个变量的实际意义。解决办法是将这些变量放入一个结构体中,从而使其用途更加清晰;</li> <li>读取文件时,使用 <code>expect</code> 处理错误,但未对其读取错误的原因进行细分,因为文件打不开可能是文件不存在,文件权限不够,文件损坏等原因;</li> <li>对于命令行参数的错误处理,若输入的参数没有两个,那么程序本身就会报错,并且能够预料到的错误一定是 <code>Out of bound</code> 这类的错误,但是对于使用者来说,可能并不清楚这个所谓的 <strong>越界错误</strong> 意味着什么,无法清晰解释错误的具体原因。因此最好要将所有错误处理集中到一起,将来开发者要考虑错误处理的时候,就只要处理这一处代码,这样也能保证为用户打印出有意义的错误信息,而不是只有程序员能看懂的 <code>Out of bound</code>。</li></ol> <h3 id="_3-2-二进制程序关注点分离的指导性原则"><a href="#_3-2-二进制程序关注点分离的指导性原则" class="header-anchor">#</a> 3.2 二进制程序关注点分离的指导性原则</h3> <ul><li>将程序拆分为 <code>main.rs</code> 和 <code>lib.rs</code>,将业务逻辑放入 <code>lib.rs</code></li> <li>当命令行解析逻辑较少时,将它放在 <code>main.rs</code> 也行</li> <li>当命令行解析逻辑变复杂时,需要将它从 <code>main.rs</code> 提取到 <code>lib.rs</code></li></ul> <p>经过上述拆分,留在 <code>main</code> 的功能有:</p> <ul><li>使用参数值调用命令行解析逻辑</li> <li>进行其它配置</li> <li>调用 <code>lib.rs</code> 中的 <code>run</code> 函数</li> <li>处理 <code>run</code> 函数可能出现的错误</li></ul> <p>因此放在 <code>main.rs</code> 中的代码量应足够小,小到直接阅读代码就可以确保代码的正确性。将业务逻辑放入 <code>lib.rs</code> 中也方便进行功能测试。</p> <p>针对上面说的四个方面的问题,我们逐一进行解决。</p> <p><strong>1. 拆分出命令行参数提取功能</strong></p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>env<span class="token punctuation">;</span>
  80. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>fs<span class="token punctuation">;</span>
  81. <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  82. <span class="token punctuation">...</span>
  83. <span class="token keyword">let</span> <span class="token punctuation">(</span>search_string<span class="token punctuation">,</span> filename<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">parse_config</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
  84. <span class="token punctuation">...</span>
  85. <span class="token punctuation">}</span>
  86. <span class="token keyword">fn</span> <span class="token function-definition function">parse_config</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span><span class="token class-name">String</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  87. <span class="token keyword">let</span> search_string <span class="token operator">=</span> <span class="token operator">&amp;</span>args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  88. <span class="token keyword">let</span> filename <span class="token operator">=</span> <span class="token operator">&amp;</span>args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  89. <span class="token punctuation">(</span>search_string<span class="token punctuation">,</span> filename<span class="token punctuation">)</span>
  90. <span class="token punctuation">}</span>
  91. </code></pre></div><p>我们发现,<code>parse_config</code> 函数目前返回一个元组,但是在主函数中,又将该元组拆分出来,赋值给两个变量,这样感觉有点“脱裤子放屁”的感觉,来回折腾。实际上这种情况就说明程序中这样设计数据结构是不正确的。因此较好的做法就是将返回的元组中的变量放入一个结构体。</p> <p><strong>2. 创建结构体</strong></p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">struct</span> <span class="token type-definition class-name">Config</span> <span class="token punctuation">{</span>
  92. search_string<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
  93. filename<span class="token punctuation">:</span> <span class="token class-name">String</span>
  94. <span class="token punctuation">}</span>
  95. <span class="token keyword">fn</span> <span class="token function-definition function">parse_config</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span><span class="token class-name">String</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  96. <span class="token keyword">let</span> search_string <span class="token operator">=</span> <span class="token operator">&amp;</span>args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  97. <span class="token keyword">let</span> filename <span class="token operator">=</span> <span class="token operator">&amp;</span>args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  98. <span class="token class-name">Config</span> <span class="token punctuation">{</span> search_string<span class="token punctuation">,</span> filename <span class="token punctuation">}</span>
  99. <span class="token punctuation">}</span>
  100. </code></pre></div><p>这里我们创建一个叫 <code>Config</code> 的结构体,将 <code>search_string</code> 和 <code>filename</code> 两个变量放入结构体。但是上面的代码会报错,这是因为在函数 <code>parse_config</code> 中,<code>args</code> 参数是切片类型,是没有所有权的(它的所有权被 <code>main</code> 函数拥有),而在最后要返回一个 <code>Config</code> 结构体对象,该结构体需要占用所有权,因此会报错。</p> <p>这里用一个简单的方法来处理,就是创建 <code>args[1]</code> 和 <code>args[2]</code> 的两个副本,尽管这样会损失性能。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">fn</span> <span class="token function-definition function">parse_config</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span><span class="token class-name">String</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  101. <span class="token keyword">let</span> search_string <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  102. <span class="token keyword">let</span> filename <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  103. <span class="token class-name">Config</span> <span class="token punctuation">{</span> search_string<span class="token punctuation">,</span> filename <span class="token punctuation">}</span>
  104. <span class="token punctuation">}</span>
  105. </code></pre></div><p>我们再来看 <code>parse_config</code> 函数,它返回的是一个结构体,实际上是要创建一个新的结构体,因此我们最好再实现该结构体的 <code>new</code> 函数。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">impl</span> <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  106. <span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span><span class="token class-name">String</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  107. <span class="token keyword">let</span> search_string <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  108. <span class="token keyword">let</span> filename <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  109. <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  110. search_string<span class="token punctuation">,</span>
  111. filename<span class="token punctuation">,</span>
  112. <span class="token punctuation">}</span>
  113. <span class="token punctuation">}</span>
  114. <span class="token punctuation">}</span>
  115. </code></pre></div><p>这里就是将刚刚的 <code>parse_config</code> 变成了结构体 <code>Config</code> 的函数。重构后的完整代码如下:</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>env<span class="token punctuation">;</span>
  116. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>fs<span class="token punctuation">;</span>
  117. <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  118. <span class="token keyword">let</span> args<span class="token punctuation">:</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token class-name">String</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token namespace">env<span class="token punctuation">::</span></span><span class="token function">args</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">collect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  119. <span class="token keyword">let</span> config <span class="token operator">=</span> <span class="token class-name">Config</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
  120. <span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token namespace">fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span>filename<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">expect</span><span class="token punctuation">(</span><span class="token string">&quot;该文件不存在&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  121. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;文件内容:\n{}&quot;</span><span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
  122. <span class="token punctuation">}</span>
  123. <span class="token keyword">struct</span> <span class="token type-definition class-name">Config</span> <span class="token punctuation">{</span>
  124. search_string<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
  125. filename<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
  126. <span class="token punctuation">}</span>
  127. <span class="token keyword">impl</span> <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  128. <span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span><span class="token class-name">String</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  129. <span class="token keyword">let</span> search_string <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  130. <span class="token keyword">let</span> filename <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  131. <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  132. search_string<span class="token punctuation">,</span>
  133. filename<span class="token punctuation">,</span>
  134. <span class="token punctuation">}</span>
  135. <span class="token punctuation">}</span>
  136. <span class="token punctuation">}</span>
  137. </code></pre></div><p><strong>3. 错误处理</strong></p> <p>我们不输入参数进行运行,不出预料的会产生下面的错误:</p> <div class="language-rust extra-class"><pre class="language-rust"><code>➜ ~<span class="token operator">/</span>code<span class="token operator">/</span>rust<span class="token operator">/</span>minigrep git<span class="token punctuation">:</span><span class="token punctuation">(</span>master<span class="token punctuation">)</span> ✗ cargo run
  138. warning<span class="token punctuation">:</span> field is never read<span class="token punctuation">:</span> `search_string`
  139. <span class="token operator">-</span><span class="token punctuation">-&gt;</span> src<span class="token operator">/</span>main<span class="token punctuation">.</span>rs<span class="token punctuation">:</span><span class="token number">19</span><span class="token punctuation">:</span><span class="token number">5</span>
  140. <span class="token operator">|</span>
  141. <span class="token number">19</span> <span class="token operator">|</span> search_string<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
  142. <span class="token closure-params"><span class="token closure-punctuation punctuation">|</span> <span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span><span class="token operator">^</span>
  143. <span class="token closure-punctuation punctuation">|</span></span>
  144. <span class="token operator">=</span> note<span class="token punctuation">:</span> `<span class="token attribute attr-name">#[warn(dead_code)]</span>` on by default
  145. warning<span class="token punctuation">:</span> `minigrep` <span class="token punctuation">(</span>bin <span class="token string">&quot;minigrep&quot;</span><span class="token punctuation">)</span> generated <span class="token number">1</span> warning
  146. <span class="token class-name">Finished</span> dev <span class="token punctuation">[</span>unoptimized <span class="token operator">+</span> debuginfo<span class="token punctuation">]</span> <span class="token function">target</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token number">0</span><span class="token punctuation">.</span>00s
  147. <span class="token class-name">Running</span> `target<span class="token operator">/</span>debug<span class="token operator">/</span>minigrep`
  148. thread <span class="token lifetime-annotation symbol">'main</span>' panicked at <span class="token lifetime-annotation symbol">'index</span> out of bounds<span class="token punctuation">:</span> the len is <span class="token number">1</span> but the index is <span class="token number">1</span>'<span class="token punctuation">,</span> src<span class="token operator">/</span>main<span class="token punctuation">.</span>rs<span class="token punctuation">:</span><span class="token number">25</span><span class="token punctuation">:</span><span class="token number">29</span>
  149. note<span class="token punctuation">:</span> run with `<span class="token constant">RUST_BACKTRACE</span><span class="token operator">=</span><span class="token number">1</span>` environment variable to display a backtrace
  150. </code></pre></div><p>即 <strong>越界错误</strong>,这对于用户来说是无法理解的,我们当然可以在 <code>new</code> 函数中添加这样的判断语句,</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">if</span> args<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">3</span> <span class="token punctuation">{</span>
  151. <span class="token macro property">panic!</span><span class="token punctuation">(</span><span class="token string">&quot;输入参数错误,请输入两个参数。&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  152. <span class="token punctuation">}</span>
  153. </code></pre></div><p>但是这样仍然会有编译器的其他信息,一般情况下,使用 <code>panic</code> 通常是程序本身的问题,但是像这类输入参数少的问题属于程序使用的问题,因此我们还需要进行改进,可以返回 <code>Result</code> 枚举,代码如下。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>env<span class="token punctuation">;</span>
  154. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>fs<span class="token punctuation">;</span>
  155. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>process<span class="token punctuation">;</span>
  156. <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  157. <span class="token keyword">let</span> args<span class="token punctuation">:</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token class-name">String</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token namespace">env<span class="token punctuation">::</span></span><span class="token function">args</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">collect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  158. <span class="token keyword">let</span> config <span class="token operator">=</span> <span class="token class-name">Config</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>args<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap_or_else</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>err<span class="token closure-punctuation punctuation">|</span></span> <span class="token punctuation">{</span>
  159. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;参数解析错误:{}&quot;</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
  160. <span class="token namespace">process<span class="token punctuation">::</span></span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  161. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  162. <span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token namespace">fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span>filename<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">expect</span><span class="token punctuation">(</span><span class="token string">&quot;该文件不存在&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  163. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;文件内容:\n{}&quot;</span><span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
  164. <span class="token punctuation">}</span>
  165. <span class="token keyword">struct</span> <span class="token type-definition class-name">Config</span> <span class="token punctuation">{</span>
  166. search_string<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
  167. filename<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
  168. <span class="token punctuation">}</span>
  169. <span class="token keyword">impl</span> <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  170. <span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span><span class="token class-name">String</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token class-name">Config</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  171. <span class="token keyword">if</span> args<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">3</span> <span class="token punctuation">{</span>
  172. <span class="token keyword">return</span> <span class="token class-name">Err</span><span class="token punctuation">(</span><span class="token string">&quot;输入参数个数不足,请输入两个参数。&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  173. <span class="token punctuation">}</span>
  174. <span class="token keyword">let</span> search_string <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  175. <span class="token keyword">let</span> filename <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  176. <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token class-name">Config</span> <span class="token punctuation">{</span>
  177. search_string<span class="token punctuation">,</span>
  178. filename<span class="token punctuation">,</span>
  179. <span class="token punctuation">}</span><span class="token punctuation">)</span>
  180. <span class="token punctuation">}</span>
  181. <span class="token punctuation">}</span>
  182. </code></pre></div><p>如果参数个数超过 2 个,则返回 <code>Err</code> 的变体,否则返回 <code>Ok</code>。主函数中,<code>unwrap_or_else</code> 函数的含义是,如果枚举返回的是 <code>Ok</code>,那么就取出 <code>Ok</code> 变体中的值返回,若枚举返回的是 <code>Err</code>,那么就调用一个闭包(匿名函数,闭包具体内容将来会介绍),然后使用 <code>process::exit(1)</code> 将程序返回,这样就不会有编译器的其他信息了。</p> <div class="language-rust extra-class"><pre class="language-rust"><code>➜ ~<span class="token operator">/</span>code<span class="token operator">/</span>rust<span class="token operator">/</span>minigrep git<span class="token punctuation">:</span><span class="token punctuation">(</span>master<span class="token punctuation">)</span> ✗ cargo run
  183. <span class="token class-name">Compiling</span> minigrep v0<span class="token number">.1</span><span class="token punctuation">.</span><span class="token number">0</span> <span class="token punctuation">(</span><span class="token operator">/</span>home<span class="token operator">/</span>cherry<span class="token operator">/</span>code<span class="token operator">/</span>rust<span class="token operator">/</span>minigrep<span class="token punctuation">)</span>
  184. <span class="token class-name">Finished</span> dev <span class="token punctuation">[</span>unoptimized <span class="token operator">+</span> debuginfo<span class="token punctuation">]</span> <span class="token function">target</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token number">0</span><span class="token punctuation">.</span>33s
  185. <span class="token class-name">Running</span> `target<span class="token operator">/</span>debug<span class="token operator">/</span>minigrep`
  186. 参数解析错误:输入参数个数不足,请输入两个参数。
  187. </code></pre></div><p><strong>4. 功能模块化</strong></p> <p>一个函数只处理一个功能,因此我们将业务逻辑(即读取文件内容)功能提取到一个新的函数中。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">fn</span> <span class="token function-definition function">run</span><span class="token punctuation">(</span>config<span class="token punctuation">:</span> <span class="token class-name">Config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  188. <span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token namespace">fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span>filename<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">expect</span><span class="token punctuation">(</span><span class="token string">&quot;该文件不存在&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  189. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;文件内容:\n{}&quot;</span><span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
  190. <span class="token punctuation">}</span>
  191. </code></pre></div><p>然后我们进行 <code>run</code> 函数的错误处理。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">fn</span> <span class="token function-definition function">run</span><span class="token punctuation">(</span>config<span class="token punctuation">:</span> <span class="token class-name">Config</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">Box</span><span class="token operator">&lt;</span><span class="token keyword">dyn</span> <span class="token class-name">Error</span><span class="token operator">&gt;&gt;</span> <span class="token punctuation">{</span>
  192. <span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token namespace">fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span>filename<span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
  193. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;文件内容:\n{}&quot;</span><span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
  194. <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  195. <span class="token punctuation">}</span>
  196. </code></pre></div><p>这里 <code>result&lt;(), Box&lt;dyn Error&gt;&gt;</code> 中第一个参数是空,第二个参数只要理解是一个实现了 <code>Error</code> 这个 <code>trait</code> 的类型,这样函数便可以在不同场景下返回不同的错误类型。</p> <p>因为 <code>expect</code> 会引起恐慌,因此将其去掉,改成 <code>?</code>,<code>?</code> 运算符遇到错误不会恐慌,它会将错误值返回给函数的调用者,如果没有发生错误,那么我们最后返回一个 <code>Ok()</code>。</p> <p>这时编译器会在 <code>run(config)</code> 出给予警告:<code>this 'Result' may be an 'Err' variant, which should be handled</code>,这说明函数返回值是一个 <code>Result</code> 类型,那么就说明可能会产生错误,因此需要对其进行处理。</p> <p><code>unwrap</code> 有打开的意思,需要从 <code>Result</code> 中提取数据,但是 <code>run</code> 函数没有返回值,因此也就不需要 <code>unwrap</code>,可以像下面这样解决这一问题。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">if</span> <span class="token keyword">let</span> <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">run</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  197. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;程序运行出错:{}&quot;</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span>
  198. <span class="token namespace">process<span class="token punctuation">::</span></span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  199. <span class="token punctuation">}</span>
  200. </code></pre></div><p>下面我们将业务逻辑迁移到 <code>lib.rs</code> 中。</p> <p><code>lib.rs:</code></p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>fs<span class="token punctuation">;</span>
  201. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span>error<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token punctuation">;</span>
  202. <span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">run</span><span class="token punctuation">(</span>config<span class="token punctuation">:</span> <span class="token class-name">Config</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">Box</span><span class="token operator">&lt;</span><span class="token keyword">dyn</span> <span class="token class-name">Error</span><span class="token operator">&gt;&gt;</span> <span class="token punctuation">{</span>
  203. <span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token namespace">fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span>filename<span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
  204. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;文件内容:\n{}&quot;</span><span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
  205. <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  206. <span class="token punctuation">}</span>
  207. <span class="token keyword">pub</span> <span class="token keyword">struct</span> <span class="token type-definition class-name">Config</span> <span class="token punctuation">{</span>
  208. <span class="token keyword">pub</span> search_string<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
  209. <span class="token keyword">pub</span> filename<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
  210. <span class="token punctuation">}</span>
  211. <span class="token keyword">impl</span> <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  212. <span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span><span class="token class-name">String</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token class-name">Config</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  213. <span class="token keyword">if</span> args<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">3</span> <span class="token punctuation">{</span>
  214. <span class="token keyword">return</span> <span class="token class-name">Err</span><span class="token punctuation">(</span><span class="token string">&quot;输入参数错误,请输入两个参数。&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  215. <span class="token punctuation">}</span>
  216. <span class="token keyword">let</span> search_string <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  217. <span class="token keyword">let</span> filename <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  218. <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token class-name">Config</span> <span class="token punctuation">{</span>
  219. search_string<span class="token punctuation">,</span>
  220. filename<span class="token punctuation">,</span>
  221. <span class="token punctuation">}</span><span class="token punctuation">)</span>
  222. <span class="token punctuation">}</span>
  223. <span class="token punctuation">}</span>
  224. </code></pre></div><p><code>main.rs:</code></p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">use</span> <span class="token namespace">minigrep<span class="token punctuation">::</span></span><span class="token class-name">Config</span><span class="token punctuation">;</span>
  225. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>env<span class="token punctuation">;</span>
  226. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>process<span class="token punctuation">;</span>
  227. <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  228. <span class="token keyword">let</span> args<span class="token punctuation">:</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token class-name">String</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token namespace">env<span class="token punctuation">::</span></span><span class="token function">args</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">collect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  229. <span class="token keyword">let</span> config <span class="token operator">=</span> <span class="token class-name">Config</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>args<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap_or_else</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>err<span class="token closure-punctuation punctuation">|</span></span> <span class="token punctuation">{</span>
  230. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;参数解析错误:{}&quot;</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
  231. <span class="token namespace">process<span class="token punctuation">::</span></span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  232. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  233. <span class="token keyword">if</span> <span class="token keyword">let</span> <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token namespace">minigrep<span class="token punctuation">::</span></span><span class="token function">run</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  234. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;程序运行出错:{}&quot;</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span>
  235. <span class="token namespace">process<span class="token punctuation">::</span></span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  236. <span class="token punctuation">}</span>
  237. <span class="token punctuation">}</span>
  238. </code></pre></div><p>要记得所有函数和结构体以及字段前都要加 <code>pub</code>,这样才能让其他 <code>crate</code> 才能进行调用。这样 <code>lib crate</code> 就有了一套公共的可用于测试的 API。</p> <p>重构到这里就基本完成了,下面就要来编写测试了。</p> <h2 id="四、使用-tdd-测试驱动开发-开发库功能"><a href="#四、使用-tdd-测试驱动开发-开发库功能" class="header-anchor">#</a> 四、使用 TDD(测试驱动开发)开发库功能</h2> <p>测试驱动开发 TDD (Test-Driven Development)</p> <ul><li>编写一个会失败的测试,运行该测试,确保它是按照预期的原因失败</li> <li>编写或修改刚好足够的代码,让新测试通过</li> <li>重构刚刚添加或修改的代码,确保测试会始终通过</li> <li>返回步骤1,继续</li></ul> <p>测试驱动开发能够对代码的设计起到指导和帮助的作用,先编写测试,然后再编写能够通过测试的代码,也能保证开发过程中能够保持测试较高的覆盖率。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">fn</span> <span class="token function-definition function">search</span><span class="token operator">&lt;</span><span class="token lifetime-annotation symbol">'a</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>query<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token punctuation">,</span> content<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token keyword">str</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token keyword">str</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  239. <span class="token keyword">let</span> <span class="token keyword">mut</span> vec <span class="token operator">=</span> <span class="token class-name">Vec</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  240. <span class="token keyword">for</span> lines <span class="token keyword">in</span> content<span class="token punctuation">.</span><span class="token function">lines</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  241. <span class="token keyword">if</span> lines<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>query<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  242. vec<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>lines<span class="token punctuation">)</span><span class="token punctuation">;</span>
  243. <span class="token punctuation">}</span>
  244. <span class="token punctuation">}</span>
  245. vec
  246. <span class="token punctuation">}</span>
  247. <span class="token attribute attr-name">#[cfg(test)]</span>
  248. <span class="token keyword">mod</span> <span class="token module-declaration namespace">test</span> <span class="token punctuation">{</span>
  249. <span class="token attribute attr-name">#[test]</span>
  250. <span class="token keyword">fn</span> <span class="token function-definition function">one_result</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  251. <span class="token keyword">use</span> <span class="token keyword">super</span><span class="token punctuation">::</span><span class="token operator">*</span><span class="token punctuation">;</span>
  252. <span class="token keyword">let</span> query <span class="token operator">=</span> <span class="token string">&quot;Lakers&quot;</span><span class="token punctuation">;</span>
  253. <span class="token keyword">let</span> contents <span class="token operator">=</span> <span class="token string">&quot;\
  254. Rust OK,
  255. Paul, James, Lakers.
  256. What a wonderful day!&quot;</span><span class="token punctuation">;</span>
  257. <span class="token macro property">assert_eq!</span><span class="token punctuation">(</span><span class="token macro property">vec!</span><span class="token punctuation">[</span><span class="token string">&quot;Paul, James, Lakers.&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function">search</span><span class="token punctuation">(</span>query<span class="token punctuation">,</span> contents<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  258. <span class="token punctuation">}</span>
  259. <span class="token punctuation">}</span>
  260. </code></pre></div><p>注意 <code>search</code> 函数中返回的引用的生命周期与 <code>content</code> 有关,而与 <code>query</code> 无关。<code>content.lines()</code> 函数返回一个的迭代器,取出文件中的每一行。这样测试代码就完成了,运行测试也是成功的。</p> <div class="language-rust extra-class"><pre class="language-rust"><code>➜ ~<span class="token operator">/</span>code<span class="token operator">/</span>rust<span class="token operator">/</span>minigrep git<span class="token punctuation">:</span><span class="token punctuation">(</span>master<span class="token punctuation">)</span> ✗ cargo test
  261. <span class="token class-name">Compiling</span> minigrep v0<span class="token number">.1</span><span class="token punctuation">.</span><span class="token number">0</span> <span class="token punctuation">(</span><span class="token operator">/</span>home<span class="token operator">/</span>cherry<span class="token operator">/</span>code<span class="token operator">/</span>rust<span class="token operator">/</span>minigrep<span class="token punctuation">)</span>
  262. <span class="token class-name">Finished</span> test <span class="token punctuation">[</span>unoptimized <span class="token operator">+</span> debuginfo<span class="token punctuation">]</span> <span class="token function">target</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token number">0</span><span class="token punctuation">.</span>36s
  263. <span class="token class-name">Running</span> <span class="token function">unittests</span> <span class="token punctuation">(</span>target<span class="token operator">/</span>debug<span class="token operator">/</span>deps<span class="token operator">/</span>minigrep<span class="token operator">-</span>662cb87b3d895995<span class="token punctuation">)</span>
  264. running <span class="token number">1</span> test
  265. test <span class="token namespace">test<span class="token punctuation">::</span></span>one_result <span class="token punctuation">...</span> ok
  266. test result<span class="token punctuation">:</span> ok<span class="token punctuation">.</span> <span class="token number">1</span> passed<span class="token punctuation">;</span> <span class="token number">0</span> failed<span class="token punctuation">;</span> <span class="token number">0</span> ignored<span class="token punctuation">;</span> <span class="token number">0</span> measured<span class="token punctuation">;</span> <span class="token number">0</span> filtered out<span class="token punctuation">;</span> finished <span class="token keyword">in</span> <span class="token number">0</span><span class="token punctuation">.</span>00s
  267. <span class="token class-name">Running</span> <span class="token function">unittests</span> <span class="token punctuation">(</span>target<span class="token operator">/</span>debug<span class="token operator">/</span>deps<span class="token operator">/</span>minigrep<span class="token operator">-</span>33abce92ed029d2f<span class="token punctuation">)</span>
  268. running <span class="token number">0</span> tests
  269. test result<span class="token punctuation">:</span> ok<span class="token punctuation">.</span> <span class="token number">0</span> passed<span class="token punctuation">;</span> <span class="token number">0</span> failed<span class="token punctuation">;</span> <span class="token number">0</span> ignored<span class="token punctuation">;</span> <span class="token number">0</span> measured<span class="token punctuation">;</span> <span class="token number">0</span> filtered out<span class="token punctuation">;</span> finished <span class="token keyword">in</span> <span class="token number">0</span><span class="token punctuation">.</span>00s
  270. <span class="token class-name">Doc</span><span class="token operator">-</span>tests minigrep
  271. running <span class="token number">0</span> tests
  272. test result<span class="token punctuation">:</span> ok<span class="token punctuation">.</span> <span class="token number">0</span> passed<span class="token punctuation">;</span> <span class="token number">0</span> failed<span class="token punctuation">;</span> <span class="token number">0</span> ignored<span class="token punctuation">;</span> <span class="token number">0</span> measured<span class="token punctuation">;</span> <span class="token number">0</span> filtered out<span class="token punctuation">;</span> finished <span class="token keyword">in</span> <span class="token number">0</span><span class="token punctuation">.</span>00s
  273. </code></pre></div><p>然后修改 <code>run</code> 函数并运行 <code>cargo run</code>。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">run</span><span class="token punctuation">(</span>config<span class="token punctuation">:</span> <span class="token class-name">Config</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">Box</span><span class="token operator">&lt;</span><span class="token keyword">dyn</span> <span class="token class-name">Error</span><span class="token operator">&gt;&gt;</span> <span class="token punctuation">{</span>
  274. <span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token namespace">fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span>filename<span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
  275. <span class="token comment">// println!(&quot;文件内容:\n{}&quot;, content);</span>
  276. <span class="token keyword">for</span> line <span class="token keyword">in</span> <span class="token function">search</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>config<span class="token punctuation">.</span>search_string<span class="token punctuation">,</span> <span class="token operator">&amp;</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  277. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;{}&quot;</span><span class="token punctuation">,</span> line<span class="token punctuation">)</span><span class="token punctuation">;</span>
  278. <span class="token punctuation">}</span>
  279. <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  280. <span class="token punctuation">}</span>
  281. </code></pre></div><div class="language-rust extra-class"><pre class="language-rust"><code>➜ ~<span class="token operator">/</span>code<span class="token operator">/</span>rust<span class="token operator">/</span>minigrep git<span class="token punctuation">:</span><span class="token punctuation">(</span>master<span class="token punctuation">)</span> ✗ cargo run is poem
  282. <span class="token class-name">Finished</span> dev <span class="token punctuation">[</span>unoptimized <span class="token operator">+</span> debuginfo<span class="token punctuation">]</span> <span class="token function">target</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token number">0</span><span class="token punctuation">.</span>00s
  283. <span class="token class-name">Running</span> `target<span class="token operator">/</span>debug<span class="token operator">/</span>minigrep is poem`
  284. <span class="token class-name">Life</span> is a broken<span class="token operator">-</span>winged bird
  285. <span class="token class-name">Life</span> is a barren field
  286. </code></pre></div><h2 id="五、使用环境变量"><a href="#五、使用环境变量" class="header-anchor">#</a> 五、使用环境变量</h2> <p>这一部分使用环境变量来实现配置选项(例如是否忽略大小写等)。</p> <p>我们首先编写一个测试:</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token attribute attr-name">#[test]</span>
  287. <span class="token keyword">fn</span> <span class="token function-definition function">case_insensitive</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  288. <span class="token keyword">let</span> query <span class="token operator">=</span> <span class="token string">&quot;LakErS&quot;</span><span class="token punctuation">;</span>
  289. <span class="token keyword">let</span> contents <span class="token operator">=</span> <span class="token string">&quot;
  290. Rust OK,
  291. Paul, James, Lakers.
  292. What a wonderful day!
  293. blakers championship&quot;</span><span class="token punctuation">;</span>
  294. <span class="token macro property">assert_eq!</span><span class="token punctuation">(</span><span class="token macro property">vec!</span><span class="token punctuation">[</span><span class="token string">&quot;Paul, James, Lakers.&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;blakers championship&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function">search_case_insensitive</span><span class="token punctuation">(</span>query<span class="token punctuation">,</span> contents<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  295. <span class="token punctuation">}</span>
  296. </code></pre></div><p>然后编写 <code>search_case_insensitive</code> 函数:</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">fn</span> <span class="token function-definition function">search_case_insensitive</span><span class="token operator">&lt;</span><span class="token lifetime-annotation symbol">'a</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>query<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token punctuation">,</span> content<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token keyword">str</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token keyword">str</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  297. <span class="token keyword">let</span> <span class="token keyword">mut</span> vec <span class="token operator">=</span> <span class="token class-name">Vec</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  298. <span class="token keyword">let</span> query <span class="token operator">=</span> query<span class="token punctuation">.</span><span class="token function">to_lowercase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  299. <span class="token keyword">for</span> lines <span class="token keyword">in</span> content<span class="token punctuation">.</span><span class="token function">lines</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  300. <span class="token keyword">if</span> lines<span class="token punctuation">.</span><span class="token function">to_lowercase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>query<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  301. vec<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>lines<span class="token punctuation">)</span><span class="token punctuation">;</span>
  302. <span class="token punctuation">}</span>
  303. <span class="token punctuation">}</span>
  304. vec
  305. <span class="token punctuation">}</span>
  306. </code></pre></div><p>其思路就是将查询的字符串和文件中的都转化成小写。</p> <p>然后我们在 <code>run</code> 函数中加入如下逻辑。</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token keyword">if</span> config<span class="token punctuation">.</span>case_sensitive <span class="token punctuation">{</span>
  307. <span class="token function">search</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>config<span class="token punctuation">.</span>search_string<span class="token punctuation">,</span> <span class="token operator">&amp;</span>content<span class="token punctuation">)</span>
  308. <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
  309. <span class="token function">search_case_insensitive</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>config<span class="token punctuation">.</span>search_string<span class="token punctuation">,</span> <span class="token operator">&amp;</span>content<span class="token punctuation">)</span>
  310. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  311. </code></pre></div><p>结构体的 <code>new</code> 函数也需要修改:</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span><span class="token class-name">String</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token class-name">Config</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  312. <span class="token keyword">if</span> args<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">3</span> <span class="token punctuation">{</span>
  313. <span class="token keyword">return</span> <span class="token class-name">Err</span><span class="token punctuation">(</span><span class="token string">&quot;输入参数错误,请输入两个参数。&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  314. <span class="token punctuation">}</span>
  315. <span class="token keyword">let</span> search_string <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  316. <span class="token keyword">let</span> filename <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  317. <span class="token keyword">let</span> case_sensitive <span class="token operator">=</span> <span class="token namespace">env<span class="token punctuation">::</span></span><span class="token function">var</span><span class="token punctuation">(</span><span class="token string">&quot;CASE_INSENSITIVE&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">is_err</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  318. <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token class-name">Config</span> <span class="token punctuation">{</span>
  319. search_string<span class="token punctuation">,</span>
  320. filename<span class="token punctuation">,</span>
  321. case_sensitive
  322. <span class="token punctuation">}</span><span class="token punctuation">)</span>
  323. <span class="token punctuation">}</span>
  324. </code></pre></div><p><code>env::var()</code> 函数返回的是 <code>Result</code> 枚举,若环境中有 <code>CASE_INSENSITIVE</code> 定义或者赋值,那么就会返回 <code>Ok</code> 中的值,我们这里只需要判断是否为 <code>Err</code> 即可。</p> <div class="language-rust extra-class"><pre class="language-rust"><code>➜ ~<span class="token operator">/</span>code<span class="token operator">/</span>rust<span class="token operator">/</span>minigrep git<span class="token punctuation">:</span><span class="token punctuation">(</span>master<span class="token punctuation">)</span> ✗ <span class="token constant">CASE_INSENSITIVE</span><span class="token operator">=</span><span class="token number">1</span> cargo run to poem
  325. <span class="token class-name">Finished</span> dev <span class="token punctuation">[</span>unoptimized <span class="token operator">+</span> debuginfo<span class="token punctuation">]</span> <span class="token function">target</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token number">0</span><span class="token punctuation">.</span>00s
  326. <span class="token class-name">Running</span> `target<span class="token operator">/</span>debug<span class="token operator">/</span>minigrep to poem`
  327. <span class="token class-name">Hold</span> fast to dreams
  328. <span class="token class-name">Hold</span> fast to dreams
  329. <span class="token class-name">To</span> see a world <span class="token keyword">in</span> a grain of sand<span class="token punctuation">,</span>
  330. </code></pre></div><h2 id="六、将错误消息写进标准错误而不是标准输出"><a href="#六、将错误消息写进标准错误而不是标准输出" class="header-anchor">#</a> 六、将错误消息写进标准错误而不是标准输出</h2> <p>当前我们都将错误信息输出到终端上,而大多数终端提供两种输出,一个是标准输出(stdout,println!),另一个叫标准错误(stderr,eprintln!)。</p> <p>我们将打印错误信息的 <code>println!</code> 改成 <code>eprintln!</code> 即可,然后运行 <code>cargo run &gt; output</code>,错误信息便不会输出到文件中,而是打印在终端了。</p> <h2 id="七、完整代码"><a href="#七、完整代码" class="header-anchor">#</a> 七、完整代码</h2> <p><strong>main.rs:</strong></p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">use</span> <span class="token namespace">minigrep<span class="token punctuation">::</span></span><span class="token class-name">Config</span><span class="token punctuation">;</span>
  331. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>env<span class="token punctuation">;</span>
  332. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>process<span class="token punctuation">;</span>
  333. <span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  334. <span class="token keyword">let</span> args<span class="token punctuation">:</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token class-name">String</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token namespace">env<span class="token punctuation">::</span></span><span class="token function">args</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">collect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  335. <span class="token keyword">let</span> config <span class="token operator">=</span> <span class="token class-name">Config</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>args<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap_or_else</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>err<span class="token closure-punctuation punctuation">|</span></span> <span class="token punctuation">{</span>
  336. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;参数解析错误:{}&quot;</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
  337. <span class="token namespace">process<span class="token punctuation">::</span></span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  338. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  339. <span class="token keyword">if</span> <span class="token keyword">let</span> <span class="token class-name">Err</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token namespace">minigrep<span class="token punctuation">::</span></span><span class="token function">run</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  340. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;程序运行出错:{}&quot;</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span>
  341. <span class="token namespace">process<span class="token punctuation">::</span></span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  342. <span class="token punctuation">}</span>
  343. <span class="token punctuation">}</span>
  344. </code></pre></div><p><strong>lib.rs:</strong></p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span>error<span class="token punctuation">::</span></span><span class="token class-name">Error</span><span class="token punctuation">;</span>
  345. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>fs<span class="token punctuation">;</span>
  346. <span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>env<span class="token punctuation">;</span>
  347. <span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">run</span><span class="token punctuation">(</span>config<span class="token punctuation">:</span> <span class="token class-name">Config</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">Box</span><span class="token operator">&lt;</span><span class="token keyword">dyn</span> <span class="token class-name">Error</span><span class="token operator">&gt;&gt;</span> <span class="token punctuation">{</span>
  348. <span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token namespace">fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span>filename<span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">;</span>
  349. <span class="token comment">// println!(&quot;文件内容:\n{}&quot;, content);</span>
  350. <span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token keyword">if</span> config<span class="token punctuation">.</span>case_sensitive <span class="token punctuation">{</span>
  351. <span class="token function">search</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>config<span class="token punctuation">.</span>search_string<span class="token punctuation">,</span> <span class="token operator">&amp;</span>content<span class="token punctuation">)</span>
  352. <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
  353. <span class="token function">search_case_insensitive</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>config<span class="token punctuation">.</span>search_string<span class="token punctuation">,</span> <span class="token operator">&amp;</span>content<span class="token punctuation">)</span>
  354. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  355. <span class="token keyword">for</span> line <span class="token keyword">in</span> result <span class="token punctuation">{</span>
  356. <span class="token macro property">println!</span><span class="token punctuation">(</span><span class="token string">&quot;{}&quot;</span><span class="token punctuation">,</span> line<span class="token punctuation">)</span><span class="token punctuation">;</span>
  357. <span class="token punctuation">}</span>
  358. <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  359. <span class="token punctuation">}</span>
  360. <span class="token keyword">pub</span> <span class="token keyword">struct</span> <span class="token type-definition class-name">Config</span> <span class="token punctuation">{</span>
  361. <span class="token keyword">pub</span> search_string<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
  362. <span class="token keyword">pub</span> filename<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
  363. <span class="token keyword">pub</span> case_sensitive<span class="token punctuation">:</span> <span class="token keyword">bool</span>
  364. <span class="token punctuation">}</span>
  365. <span class="token keyword">impl</span> <span class="token class-name">Config</span> <span class="token punctuation">{</span>
  366. <span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span><span class="token class-name">String</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token class-name">Config</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  367. <span class="token keyword">if</span> args<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token number">3</span> <span class="token punctuation">{</span>
  368. <span class="token keyword">return</span> <span class="token class-name">Err</span><span class="token punctuation">(</span><span class="token string">&quot;输入参数错误,请输入两个参数。&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  369. <span class="token punctuation">}</span>
  370. <span class="token keyword">let</span> search_string <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  371. <span class="token keyword">let</span> filename <span class="token operator">=</span> args<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  372. <span class="token keyword">let</span> case_sensitive <span class="token operator">=</span> <span class="token namespace">env<span class="token punctuation">::</span></span><span class="token function">var</span><span class="token punctuation">(</span><span class="token string">&quot;CASE_INSENSITIVE&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">is_err</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  373. <span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token class-name">Config</span> <span class="token punctuation">{</span>
  374. search_string<span class="token punctuation">,</span>
  375. filename<span class="token punctuation">,</span>
  376. case_sensitive
  377. <span class="token punctuation">}</span><span class="token punctuation">)</span>
  378. <span class="token punctuation">}</span>
  379. <span class="token punctuation">}</span>
  380. <span class="token keyword">fn</span> <span class="token function-definition function">search</span><span class="token operator">&lt;</span><span class="token lifetime-annotation symbol">'a</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>query<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token punctuation">,</span> content<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token keyword">str</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token keyword">str</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  381. <span class="token keyword">let</span> <span class="token keyword">mut</span> vec <span class="token operator">=</span> <span class="token class-name">Vec</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  382. <span class="token keyword">for</span> lines <span class="token keyword">in</span> content<span class="token punctuation">.</span><span class="token function">lines</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  383. <span class="token keyword">if</span> lines<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>query<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  384. vec<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>lines<span class="token punctuation">)</span><span class="token punctuation">;</span>
  385. <span class="token punctuation">}</span>
  386. <span class="token punctuation">}</span>
  387. vec
  388. <span class="token punctuation">}</span>
  389. <span class="token keyword">fn</span> <span class="token function-definition function">search_case_insensitive</span><span class="token operator">&lt;</span><span class="token lifetime-annotation symbol">'a</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>query<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token punctuation">,</span> content<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token keyword">str</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token keyword">str</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  390. <span class="token keyword">let</span> <span class="token keyword">mut</span> vec <span class="token operator">=</span> <span class="token class-name">Vec</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  391. <span class="token keyword">let</span> query <span class="token operator">=</span> query<span class="token punctuation">.</span><span class="token function">to_lowercase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  392. <span class="token keyword">for</span> lines <span class="token keyword">in</span> content<span class="token punctuation">.</span><span class="token function">lines</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  393. <span class="token keyword">if</span> lines<span class="token punctuation">.</span><span class="token function">to_lowercase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>query<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  394. vec<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>lines<span class="token punctuation">)</span><span class="token punctuation">;</span>
  395. <span class="token punctuation">}</span>
  396. <span class="token punctuation">}</span>
  397. vec
  398. <span class="token punctuation">}</span>
  399. <span class="token attribute attr-name">#[cfg(test)]</span>
  400. <span class="token keyword">mod</span> <span class="token module-declaration namespace">test</span> <span class="token punctuation">{</span>
  401. <span class="token keyword">use</span> <span class="token keyword">super</span><span class="token punctuation">::</span><span class="token operator">*</span><span class="token punctuation">;</span>
  402. <span class="token attribute attr-name">#[test]</span>
  403. <span class="token keyword">fn</span> <span class="token function-definition function">one_result</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  404. <span class="token keyword">let</span> query <span class="token operator">=</span> <span class="token string">&quot;Lakers&quot;</span><span class="token punctuation">;</span>
  405. <span class="token keyword">let</span> contents <span class="token operator">=</span> <span class="token string">&quot;\
  406. Rust OK,
  407. Paul, James, Lakers.
  408. What a wonderful day!&quot;</span><span class="token punctuation">;</span>
  409. <span class="token macro property">assert_eq!</span><span class="token punctuation">(</span><span class="token macro property">vec!</span><span class="token punctuation">[</span><span class="token string">&quot;Paul, James, Lakers.&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function">search</span><span class="token punctuation">(</span>query<span class="token punctuation">,</span> contents<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  410. <span class="token punctuation">}</span>
  411. <span class="token attribute attr-name">#[test]</span>
  412. <span class="token keyword">fn</span> <span class="token function-definition function">case_insensitive</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  413. <span class="token keyword">let</span> query <span class="token operator">=</span> <span class="token string">&quot;LakErS&quot;</span><span class="token punctuation">;</span>
  414. <span class="token keyword">let</span> contents <span class="token operator">=</span> <span class="token string">&quot;
  415. Rust OK,
  416. Paul, James, Lakers.
  417. What a wonderful day!
  418. blakers championship&quot;</span><span class="token punctuation">;</span>
  419. <span class="token macro property">assert_eq!</span><span class="token punctuation">(</span>
  420. <span class="token macro property">vec!</span><span class="token punctuation">[</span><span class="token string">&quot;Paul, James, Lakers.&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;blakers championship&quot;</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  421. <span class="token function">search_case_insensitive</span><span class="token punctuation">(</span>query<span class="token punctuation">,</span> contents<span class="token punctuation">)</span>
  422. <span class="token punctuation">)</span><span class="token punctuation">;</span>
  423. <span class="token punctuation">}</span>
  424. <span class="token punctuation">}</span>
  425. </code></pre></div><h2 id="八、案例-代码统计"><a href="#八、案例-代码统计" class="header-anchor">#</a> 八、案例:代码统计</h2> <h3 id="_8-1-基本功能介绍"><a href="#_8-1-基本功能介绍" class="header-anchor">#</a> 8.1 基本功能介绍</h3> <p>代码统计以给定的输入参数作为统计对象(可以是文件或文件夹),根据文件后缀名统计代码所使用的语言(暂定只统计 C、C/C++ 头文件、C++、Java、Python、Rust、汇编语言、makefile 脚本),然后统计每一种代码文件的有效代码行数、注释行和空行。没有后缀名的文件默认不进行统计。</p> <h3 id="_8-2-可拓展功能"><a href="#_8-2-可拓展功能" class="header-anchor">#</a> 8.2 可拓展功能</h3> <ul><li>丰富统计的语言种类</li> <li>命令行中利用参数指定要统计的语言,只统计指定的语言</li> <li>统计若干种编程语言的有效代码行数,空行和注释</li> <li>可以对单个文件、多个文件进行统计</li> <li>支持一些参数,例如:排除某些文件或文件夹</li> <li>可以对文件夹进行递归统计</li> <li>对压缩文件包进行递归统计</li> <li>直观展示统计结果</li> <li>将统计结果导出为图片或 PDF</li> <li>利用多线程加速代码统计</li></ul> <h3 id="_8-3-代码仓库及说明"><a href="#_8-3-代码仓库及说明" class="header-anchor">#</a> 8.3 代码仓库及说明</h3> <p>由于笔者时间有限,目前只完成了一些简单的功能,但是完全可用。2022 年 10 月之后就没有再写了,不过后续可能会有计划再完善该项目,目前该项目的代码在 <a href="https://github.com/CherryYang05/mcloc" target="_blank" rel="noopener noreferrer">Github仓库<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 中,初学者可以以此巩固一下基本语法,如果有兴趣可以帮助完善一下功能,编程老鸟就可以完全不必理会这个项目(笑)。</p></div> <footer class="page-edit"><!----> <!----></footer> <!----> </main></div><div class="global-ui"></div></div>
  426. <script src="/rust_camp_tutorial/assets/js/app.d7ab8f65.js" defer></script><script src="/rust_camp_tutorial/assets/js/2.3dc1b8de.js" defer></script><script src="/rust_camp_tutorial/assets/js/1.7f771cfb.js" defer></script><script src="/rust_camp_tutorial/assets/js/27.928f1e6b.js" defer></script>
  427. </body>
  428. </html>