trpl-zh-cn/ch13-03-improving-our-io-project.html
2024-11-07 11:53:09 +00:00

803 lines
60 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE HTML>
<html lang="en" class="light" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>改进之前的 I/O 项目 - Rust 程序设计语言 简体中文版</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="ferris.css">
<link rel="stylesheet" href="theme/2018-edition.css">
<link rel="stylesheet" href="theme/semantic-notes.css">
<link rel="stylesheet" href="theme/listing.css">
</head>
<body class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('light')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><a href="title-page.html">Rust 程序设计语言</a></li><li class="chapter-item expanded affix "><a href="foreword.html">前言</a></li><li class="chapter-item expanded affix "><a href="ch00-00-introduction.html">简介</a></li><li class="chapter-item expanded "><a href="ch01-00-getting-started.html"><strong aria-hidden="true">1.</strong> 入门指南</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch01-01-installation.html"><strong aria-hidden="true">1.1.</strong> 安装</a></li><li class="chapter-item expanded "><a href="ch01-02-hello-world.html"><strong aria-hidden="true">1.2.</strong> Hello, World!</a></li><li class="chapter-item expanded "><a href="ch01-03-hello-cargo.html"><strong aria-hidden="true">1.3.</strong> Hello, Cargo!</a></li></ol></li><li class="chapter-item expanded "><a href="ch02-00-guessing-game-tutorial.html"><strong aria-hidden="true">2.</strong> 写个猜数字游戏</a></li><li class="chapter-item expanded "><a href="ch03-00-common-programming-concepts.html"><strong aria-hidden="true">3.</strong> 常见编程概念</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch03-01-variables-and-mutability.html"><strong aria-hidden="true">3.1.</strong> 变量与可变性</a></li><li class="chapter-item expanded "><a href="ch03-02-data-types.html"><strong aria-hidden="true">3.2.</strong> 数据类型</a></li><li class="chapter-item expanded "><a href="ch03-03-how-functions-work.html"><strong aria-hidden="true">3.3.</strong> 函数</a></li><li class="chapter-item expanded "><a href="ch03-04-comments.html"><strong aria-hidden="true">3.4.</strong> 注释</a></li><li class="chapter-item expanded "><a href="ch03-05-control-flow.html"><strong aria-hidden="true">3.5.</strong> 控制流</a></li></ol></li><li class="chapter-item expanded "><a href="ch04-00-understanding-ownership.html"><strong aria-hidden="true">4.</strong> 认识所有权</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch04-01-what-is-ownership.html"><strong aria-hidden="true">4.1.</strong> 什么是所有权?</a></li><li class="chapter-item expanded "><a href="ch04-02-references-and-borrowing.html"><strong aria-hidden="true">4.2.</strong> 引用与借用</a></li><li class="chapter-item expanded "><a href="ch04-03-slices.html"><strong aria-hidden="true">4.3.</strong> Slice 类型</a></li></ol></li><li class="chapter-item expanded "><a href="ch05-00-structs.html"><strong aria-hidden="true">5.</strong> 使用结构体组织相关联的数据</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch05-01-defining-structs.html"><strong aria-hidden="true">5.1.</strong> 结构体的定义和实例化</a></li><li class="chapter-item expanded "><a href="ch05-02-example-structs.html"><strong aria-hidden="true">5.2.</strong> 结构体示例程序</a></li><li class="chapter-item expanded "><a href="ch05-03-method-syntax.html"><strong aria-hidden="true">5.3.</strong> 方法语法</a></li></ol></li><li class="chapter-item expanded "><a href="ch06-00-enums.html"><strong aria-hidden="true">6.</strong> 枚举和模式匹配</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch06-01-defining-an-enum.html"><strong aria-hidden="true">6.1.</strong> 枚举的定义</a></li><li class="chapter-item expanded "><a href="ch06-02-match.html"><strong aria-hidden="true">6.2.</strong> match 控制流结构</a></li><li class="chapter-item expanded "><a href="ch06-03-if-let.html"><strong aria-hidden="true">6.3.</strong> if let 简洁控制流</a></li></ol></li><li class="chapter-item expanded "><a href="ch07-00-managing-growing-projects-with-packages-crates-and-modules.html"><strong aria-hidden="true">7.</strong> 使用包、Crate 和模块管理不断增长的项目</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch07-01-packages-and-crates.html"><strong aria-hidden="true">7.1.</strong> 包和 Crate</a></li><li class="chapter-item expanded "><a href="ch07-02-defining-modules-to-control-scope-and-privacy.html"><strong aria-hidden="true">7.2.</strong> 定义模块来控制作用域与私有性</a></li><li class="chapter-item expanded "><a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html"><strong aria-hidden="true">7.3.</strong> 引用模块项目的路径</a></li><li class="chapter-item expanded "><a href="ch07-04-bringing-paths-into-scope-with-the-use-keyword.html"><strong aria-hidden="true">7.4.</strong> 使用 use 关键字将路径引入作用域</a></li><li class="chapter-item expanded "><a href="ch07-05-separating-modules-into-different-files.html"><strong aria-hidden="true">7.5.</strong> 将模块拆分成多个文件</a></li></ol></li><li class="chapter-item expanded "><a href="ch08-00-common-collections.html"><strong aria-hidden="true">8.</strong> 常见集合</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch08-01-vectors.html"><strong aria-hidden="true">8.1.</strong> 使用 Vector 储存列表</a></li><li class="chapter-item expanded "><a href="ch08-02-strings.html"><strong aria-hidden="true">8.2.</strong> 使用字符串储存 UTF-8 编码的文本</a></li><li class="chapter-item expanded "><a href="ch08-03-hash-maps.html"><strong aria-hidden="true">8.3.</strong> 使用 Hash Map 储存键值对</a></li></ol></li><li class="chapter-item expanded "><a href="ch09-00-error-handling.html"><strong aria-hidden="true">9.</strong> 错误处理</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch09-01-unrecoverable-errors-with-panic.html"><strong aria-hidden="true">9.1.</strong> 用 panic! 处理不可恢复的错误</a></li><li class="chapter-item expanded "><a href="ch09-02-recoverable-errors-with-result.html"><strong aria-hidden="true">9.2.</strong> 用 Result 处理可恢复的错误</a></li><li class="chapter-item expanded "><a href="ch09-03-to-panic-or-not-to-panic.html"><strong aria-hidden="true">9.3.</strong> 要不要 panic!</a></li></ol></li><li class="chapter-item expanded "><a href="ch10-00-generics.html"><strong aria-hidden="true">10.</strong> 泛型、Trait 和生命周期</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch10-01-syntax.html"><strong aria-hidden="true">10.1.</strong> 泛型数据类型</a></li><li class="chapter-item expanded "><a href="ch10-02-traits.html"><strong aria-hidden="true">10.2.</strong> Trait定义共同行为</a></li><li class="chapter-item expanded "><a href="ch10-03-lifetime-syntax.html"><strong aria-hidden="true">10.3.</strong> 生命周期确保引用有效</a></li></ol></li><li class="chapter-item expanded "><a href="ch11-00-testing.html"><strong aria-hidden="true">11.</strong> 编写自动化测试</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch11-01-writing-tests.html"><strong aria-hidden="true">11.1.</strong> 如何编写测试</a></li><li class="chapter-item expanded "><a href="ch11-02-running-tests.html"><strong aria-hidden="true">11.2.</strong> 控制测试如何运行</a></li><li class="chapter-item expanded "><a href="ch11-03-test-organization.html"><strong aria-hidden="true">11.3.</strong> 测试的组织结构</a></li></ol></li><li class="chapter-item expanded "><a href="ch12-00-an-io-project.html"><strong aria-hidden="true">12.</strong> 一个 I/O 项目:构建命令行程序</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch12-01-accepting-command-line-arguments.html"><strong aria-hidden="true">12.1.</strong> 接受命令行参数</a></li><li class="chapter-item expanded "><a href="ch12-02-reading-a-file.html"><strong aria-hidden="true">12.2.</strong> 读取文件</a></li><li class="chapter-item expanded "><a href="ch12-03-improving-error-handling-and-modularity.html"><strong aria-hidden="true">12.3.</strong> 重构以改进模块化与错误处理</a></li><li class="chapter-item expanded "><a href="ch12-04-testing-the-librarys-functionality.html"><strong aria-hidden="true">12.4.</strong> 采用测试驱动开发完善库的功能</a></li><li class="chapter-item expanded "><a href="ch12-05-working-with-environment-variables.html"><strong aria-hidden="true">12.5.</strong> 处理环境变量</a></li><li class="chapter-item expanded "><a href="ch12-06-writing-to-stderr-instead-of-stdout.html"><strong aria-hidden="true">12.6.</strong> 将错误信息输出到标准错误而不是标准输出</a></li></ol></li><li class="chapter-item expanded "><a href="ch13-00-functional-features.html"><strong aria-hidden="true">13.</strong> Rust 中的函数式语言功能:迭代器与闭包</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch13-01-closures.html"><strong aria-hidden="true">13.1.</strong> 闭包:可以捕获其环境的匿名函数</a></li><li class="chapter-item expanded "><a href="ch13-02-iterators.html"><strong aria-hidden="true">13.2.</strong> 使用迭代器处理元素序列</a></li><li class="chapter-item expanded "><a href="ch13-03-improving-our-io-project.html" class="active"><strong aria-hidden="true">13.3.</strong> 改进之前的 I/O 项目</a></li><li class="chapter-item expanded "><a href="ch13-04-performance.html"><strong aria-hidden="true">13.4.</strong> 性能比较:循环对迭代器</a></li></ol></li><li class="chapter-item expanded "><a href="ch14-00-more-about-cargo.html"><strong aria-hidden="true">14.</strong> 更多关于 Cargo 和 Crates.io 的内容</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch14-01-release-profiles.html"><strong aria-hidden="true">14.1.</strong> 采用发布配置自定义构建</a></li><li class="chapter-item expanded "><a href="ch14-02-publishing-to-crates-io.html"><strong aria-hidden="true">14.2.</strong> 将 crate 发布到 Crates.io</a></li><li class="chapter-item expanded "><a href="ch14-03-cargo-workspaces.html"><strong aria-hidden="true">14.3.</strong> Cargo 工作空间</a></li><li class="chapter-item expanded "><a href="ch14-04-installing-binaries.html"><strong aria-hidden="true">14.4.</strong> 使用 cargo install 安装二进制文件</a></li><li class="chapter-item expanded "><a href="ch14-05-extending-cargo.html"><strong aria-hidden="true">14.5.</strong> Cargo 自定义扩展命令</a></li></ol></li><li class="chapter-item expanded "><a href="ch15-00-smart-pointers.html"><strong aria-hidden="true">15.</strong> 智能指针</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch15-01-box.html"><strong aria-hidden="true">15.1.</strong> 使用 Box&lt;T&gt; 指向堆上数据</a></li><li class="chapter-item expanded "><a href="ch15-02-deref.html"><strong aria-hidden="true">15.2.</strong> 使用 Deref Trait 将智能指针当作常规引用处理</a></li><li class="chapter-item expanded "><a href="ch15-03-drop.html"><strong aria-hidden="true">15.3.</strong> 使用 Drop Trait 运行清理代码</a></li><li class="chapter-item expanded "><a href="ch15-04-rc.html"><strong aria-hidden="true">15.4.</strong> Rc&lt;T&gt; 引用计数智能指针</a></li><li class="chapter-item expanded "><a href="ch15-05-interior-mutability.html"><strong aria-hidden="true">15.5.</strong> RefCell&lt;T&gt; 与内部可变性模式</a></li><li class="chapter-item expanded "><a href="ch15-06-reference-cycles.html"><strong aria-hidden="true">15.6.</strong> 引用循环会导致内存泄漏</a></li></ol></li><li class="chapter-item expanded "><a href="ch16-00-concurrency.html"><strong aria-hidden="true">16.</strong> 无畏并发</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch16-01-threads.html"><strong aria-hidden="true">16.1.</strong> 使用线程同时地运行代码</a></li><li class="chapter-item expanded "><a href="ch16-02-message-passing.html"><strong aria-hidden="true">16.2.</strong> 使用消息传递在线程间通信</a></li><li class="chapter-item expanded "><a href="ch16-03-shared-state.html"><strong aria-hidden="true">16.3.</strong> 共享状态并发</a></li><li class="chapter-item expanded "><a href="ch16-04-extensible-concurrency-sync-and-send.html"><strong aria-hidden="true">16.4.</strong> 使用 Sync 与 Send Traits 的可扩展并发</a></li></ol></li><li class="chapter-item expanded "><a href="ch17-00-async-await.html"><strong aria-hidden="true">17.</strong> Async 和 await</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch17-01-futures-and-syntax.html"><strong aria-hidden="true">17.1.</strong> Futures 和 async 语法</a></li><li class="chapter-item expanded "><a href="ch17-02-concurrency-with-async.html"><strong aria-hidden="true">17.2.</strong> 并发与 async</a></li><li class="chapter-item expanded "><a href="ch17-03-more-futures.html"><strong aria-hidden="true">17.3.</strong> 使用任意数量的 futures</a></li><li class="chapter-item expanded "><a href="ch17-04-streams.html"><strong aria-hidden="true">17.4.</strong>Streams</a></li><li class="chapter-item expanded "><a href="ch17-05-traits-for-async.html"><strong aria-hidden="true">17.5.</strong> 深入理解 async 相关的 traits</a></li><li class="chapter-item expanded "><a href="ch17-06-futures-tasks-threads.html"><strong aria-hidden="true">17.6.</strong> Futures任务tasks和线程threads</a></li></ol></li><li class="chapter-item expanded "><a href="ch18-00-oop.html"><strong aria-hidden="true">18.</strong> Rust 的面向对象编程特性</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch18-01-what-is-oo.html"><strong aria-hidden="true">18.1.</strong> 面向对象语言的特点</a></li><li class="chapter-item expanded "><a href="ch18-02-trait-objects.html"><strong aria-hidden="true">18.2.</strong> 顾及不同类型值的 trait 对象</a></li><li class="chapter-item expanded "><a href="ch18-03-oo-design-patterns.html"><strong aria-hidden="true">18.3.</strong> 面向对象设计模式的实现</a></li></ol></li><li class="chapter-item expanded "><a href="ch19-00-patterns.html"><strong aria-hidden="true">19.</strong> 模式与模式匹配</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch19-01-all-the-places-for-patterns.html"><strong aria-hidden="true">19.1.</strong> 所有可能会用到模式的位置</a></li><li class="chapter-item expanded "><a href="ch19-02-refutability.html"><strong aria-hidden="true">19.2.</strong> Refutability可反驳性: 模式是否会匹配失效</a></li><li class="chapter-item expanded "><a href="ch19-03-pattern-syntax.html"><strong aria-hidden="true">19.3.</strong> 模式语法</a></li></ol></li><li class="chapter-item expanded "><a href="ch20-00-advanced-features.html"><strong aria-hidden="true">20.</strong> 高级特征</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch20-01-unsafe-rust.html"><strong aria-hidden="true">20.1.</strong> 不安全的 Rust</a></li><li class="chapter-item expanded "><a href="ch20-03-advanced-traits.html"><strong aria-hidden="true">20.2.</strong> 高级 trait</a></li><li class="chapter-item expanded "><a href="ch20-04-advanced-types.html"><strong aria-hidden="true">20.3.</strong> 高级类型</a></li><li class="chapter-item expanded "><a href="ch20-05-advanced-functions-and-closures.html"><strong aria-hidden="true">20.4.</strong> 高级函数与闭包</a></li><li class="chapter-item expanded "><a href="ch20-06-macros.html"><strong aria-hidden="true">20.5.</strong></a></li></ol></li><li class="chapter-item expanded "><a href="ch21-00-final-project-a-web-server.html"><strong aria-hidden="true">21.</strong> 最后的项目:构建多线程 web server</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch21-01-single-threaded.html"><strong aria-hidden="true">21.1.</strong> 建立单线程 web server</a></li><li class="chapter-item expanded "><a href="ch21-02-multithreaded.html"><strong aria-hidden="true">21.2.</strong> 将单线程 server 变为多线程 server</a></li><li class="chapter-item expanded "><a href="ch21-03-graceful-shutdown-and-cleanup.html"><strong aria-hidden="true">21.3.</strong> 优雅停机与清理</a></li></ol></li><li class="chapter-item expanded "><a href="appendix-00.html"><strong aria-hidden="true">22.</strong> 附录</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="appendix-01-keywords.html"><strong aria-hidden="true">22.1.</strong> A - 关键字</a></li><li class="chapter-item expanded "><a href="appendix-02-operators.html"><strong aria-hidden="true">22.2.</strong> B - 运算符与符号</a></li><li class="chapter-item expanded "><a href="appendix-03-derivable-traits.html"><strong aria-hidden="true">22.3.</strong> C - 可派生的 trait</a></li><li class="chapter-item expanded "><a href="appendix-04-useful-development-tools.html"><strong aria-hidden="true">22.4.</strong> D - 实用开发工具</a></li><li class="chapter-item expanded "><a href="appendix-05-editions.html"><strong aria-hidden="true">22.5.</strong> E - 版本</a></li><li class="chapter-item expanded "><a href="appendix-06-translation.html"><strong aria-hidden="true">22.6.</strong> F - 本书译本</a></li><li class="chapter-item expanded "><a href="appendix-07-nightly-rust.html"><strong aria-hidden="true">22.7.</strong> G - Rust 是如何开发的与 “Nightly Rust”</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust 程序设计语言 简体中文版</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/KaiserY/trpl-zh-cn/tree/main" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/KaiserY/trpl-zh-cn/edit/main/src/ch13-03-improving-our-io-project.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h2 id="改进-io-项目"><a class="header" href="#改进-io-项目">改进 I/O 项目</a></h2>
<blockquote>
<p><a href="https://github.com/rust-lang/book/blob/main/src/ch13-03-improving-our-io-project.md">ch13-03-improving-our-io-project.md</a>
<br>
commit 2cd1b5593d26dc6a03c20f8619187ad4b2485552</p>
</blockquote>
<p>掌握了这些关于迭代器的新知识后,我们可以使用迭代器来改进第十二章中 I/O 项目的实现来使得代码更简洁明了。接下来,让我们看看迭代器如何改进 <code>Config::build</code> 函数和 <code>search</code> 函数的实现。</p>
<h3 id="使用迭代器去除-clone"><a class="header" href="#使用迭代器去除-clone">使用迭代器去除 <code>clone</code></a></h3>
<p>在示例 12-6 中,我们增加了一些代码获取一个 <code>String</code> 类型的 slice 并创建一个 <code>Config</code> 结构体的实例,它们索引 slice 中的值并克隆这些值以便 <code>Config</code> 结构体可以拥有这些值。在示例 13-17 中重现了第十二章结尾示例 12-23 中 <code>Config::build</code> 函数的实现:</p>
<p><span class="filename">文件名src/lib.rs</span></p>
<pre><code class="language-rust ignore"><span class="boring">use std::env;
</span><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring"> pub ignore_case: bool,
</span><span class="boring">}
</span><span class="boring">
</span>impl Config {
pub fn build(args: &amp;[String]) -&gt; Result&lt;Config, &amp;'static str&gt; {
if args.len() &lt; 3 {
return Err("not enough arguments");
}
let query = args[1].clone();
let file_path = args[2].clone();
let ignore_case = env::var("IGNORE_CASE").is_ok();
Ok(Config {
query,
file_path,
ignore_case,
})
}
}
<span class="boring">
</span><span class="boring">pub fn run(config: Config) -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
</span><span class="boring">
</span><span class="boring"> let results = if config.ignore_case {
</span><span class="boring"> search_case_insensitive(&amp;config.query, &amp;contents)
</span><span class="boring"> } else {
</span><span class="boring"> search(&amp;config.query, &amp;contents)
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> for line in results {
</span><span class="boring"> println!("{line}");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> Ok(())
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn search&lt;'a&gt;(query: &amp;str, contents: &amp;'a str) -&gt; Vec&lt;&amp;'a str&gt; {
</span><span class="boring"> let mut results = Vec::new();
</span><span class="boring">
</span><span class="boring"> for line in contents.lines() {
</span><span class="boring"> if line.contains(query) {
</span><span class="boring"> results.push(line);
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> results
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn search_case_insensitive&lt;'a&gt;(
</span><span class="boring"> query: &amp;str,
</span><span class="boring"> contents: &amp;'a str,
</span><span class="boring">) -&gt; Vec&lt;&amp;'a str&gt; {
</span><span class="boring"> let query = query.to_lowercase();
</span><span class="boring"> let mut results = Vec::new();
</span><span class="boring">
</span><span class="boring"> for line in contents.lines() {
</span><span class="boring"> if line.to_lowercase().contains(&amp;query) {
</span><span class="boring"> results.push(line);
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> results
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn case_sensitive() {
</span><span class="boring"> let query = "duct";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.
</span><span class="boring">Duct tape.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(vec!["safe, fast, productive."], search(query, contents));
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn case_insensitive() {
</span><span class="boring"> let query = "rUsT";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.
</span><span class="boring">Trust me.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(
</span><span class="boring"> vec!["Rust:", "Trust me."],
</span><span class="boring"> search_case_insensitive(query, contents)
</span><span class="boring"> );
</span><span class="boring"> }
</span><span class="boring">}</span></code></pre>
<p><span class="caption">示例 13-17重现示例 12-23 的 <code>Config::build</code> 函数</span></p>
<p>当时我们说过不必担心低效的 <code>clone</code> 调用,因为我们以后会将其移除。好吧,就是现在!</p>
<p>起初这里需要 <code>clone</code> 的原因是参数 <code>args</code> 中有一个 <code>String</code> 元素的 slice<code>build</code> 函数并不拥有 <code>args</code>。为了能够返回 <code>Config</code> 实例的所有权,我们需要克隆 <code>Config</code> 中字段 <code>query</code><code>file_path</code> 的值,这样 <code>Config</code> 实例就能拥有这些值。</p>
<p>在学习了迭代器之后,我们可以将 <code>build</code> 函数改为获取一个有所有权的迭代器作为参数,而不是借用 slice。我们将使用迭代器功能代替之前检查 slice 长度和索引特定位置的代码。这样可以更清晰地表达 <code>Config::build</code> 函数的操作,因为迭代器会负责访问这些值。</p>
<p>一旦 <code>Config::build</code> 获取了迭代器的所有权并不再使用借用的索引操作,就可以将迭代器中的 <code>String</code> 值移动到 <code>Config</code> 中,而不是调用 <code>clone</code> 分配新的空间。</p>
<h4 id="直接使用返回的迭代器"><a class="header" href="#直接使用返回的迭代器">直接使用返回的迭代器</a></h4>
<p>打开 I/O 项目的 <em>src/main.rs</em> 文件,它看起来应该像这样:</p>
<p><span class="filename">文件名src/main.rs</span></p>
<pre><code class="language-rust ignore"><span class="boring">use std::env;
</span><span class="boring">use std::process;
</span><span class="boring">
</span><span class="boring">use minigrep::Config;
</span><span class="boring">
</span>fn main() {
let args: Vec&lt;String&gt; = env::args().collect();
let config = Config::build(&amp;args).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {err}");
process::exit(1);
});
// --snip--
<span class="boring">
</span><span class="boring"> if let Err(e) = minigrep::run(config) {
</span><span class="boring"> eprintln!("Application error: {e}");
</span><span class="boring"> process::exit(1);
</span><span class="boring"> }
</span>}</code></pre>
<p>首先我们修改第十二章结尾示例 12-24 中的 <code>main</code> 函数的开头为示例 13-18 中的代码。在更新 <code>Config::build</code> 之前这些代码还不能编译:</p>
<p><span class="filename">文件名src/main.rs</span></p>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">use std::env;
</span><span class="boring">use std::process;
</span><span class="boring">
</span><span class="boring">use minigrep::Config;
</span><span class="boring">
</span>fn main() {
let config = Config::build(env::args()).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {err}");
process::exit(1);
});
// --snip--
<span class="boring">
</span><span class="boring"> if let Err(e) = minigrep::run(config) {
</span><span class="boring"> eprintln!("Application error: {e}");
</span><span class="boring"> process::exit(1);
</span><span class="boring"> }
</span>}</code></pre>
<p><span class="caption">示例 13-25<code>env::args</code> 的返回值传递给 <code>Config::build</code></span></p>
<p><code>env::args</code> 函数返回一个迭代器!不同于将迭代器的值收集到一个 vector 中接着传递一个 slice 给 <code>Config::build</code>,现在我们直接将 <code>env::args</code> 返回的迭代器的所有权传递给 <code>Config::build</code></p>
<p>接下来需要更新 <code>Config::build</code> 的定义。在 I/O 项目的 <em>src/lib.rs</em> 中,将 <code>Config::build</code> 的签名改为如示例 13-19 所示。这仍然不能编译因为我们还需更新函数体。</p>
<p><span class="filename">文件名src/lib.rs</span></p>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">use std::env;
</span><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring"> pub ignore_case: bool,
</span><span class="boring">}
</span><span class="boring">
</span>impl Config {
pub fn build(
mut args: impl Iterator&lt;Item = String&gt;,
) -&gt; Result&lt;Config, &amp;'static str&gt; {
// --snip--
<span class="boring"> if args.len() &lt; 3 {
</span><span class="boring"> return Err("not enough arguments");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let query = args[1].clone();
</span><span class="boring"> let file_path = args[2].clone();
</span><span class="boring">
</span><span class="boring"> let ignore_case = env::var("IGNORE_CASE").is_ok();
</span><span class="boring">
</span><span class="boring"> Ok(Config {
</span><span class="boring"> query,
</span><span class="boring"> file_path,
</span><span class="boring"> ignore_case,
</span><span class="boring"> })
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn run(config: Config) -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
</span><span class="boring">
</span><span class="boring"> let results = if config.ignore_case {
</span><span class="boring"> search_case_insensitive(&amp;config.query, &amp;contents)
</span><span class="boring"> } else {
</span><span class="boring"> search(&amp;config.query, &amp;contents)
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> for line in results {
</span><span class="boring"> println!("{line}");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> Ok(())
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn search&lt;'a&gt;(query: &amp;str, contents: &amp;'a str) -&gt; Vec&lt;&amp;'a str&gt; {
</span><span class="boring"> let mut results = Vec::new();
</span><span class="boring">
</span><span class="boring"> for line in contents.lines() {
</span><span class="boring"> if line.contains(query) {
</span><span class="boring"> results.push(line);
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> results
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn search_case_insensitive&lt;'a&gt;(
</span><span class="boring"> query: &amp;str,
</span><span class="boring"> contents: &amp;'a str,
</span><span class="boring">) -&gt; Vec&lt;&amp;'a str&gt; {
</span><span class="boring"> let query = query.to_lowercase();
</span><span class="boring"> let mut results = Vec::new();
</span><span class="boring">
</span><span class="boring"> for line in contents.lines() {
</span><span class="boring"> if line.to_lowercase().contains(&amp;query) {
</span><span class="boring"> results.push(line);
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> results
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn case_sensitive() {
</span><span class="boring"> let query = "duct";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.
</span><span class="boring">Duct tape.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(vec!["safe, fast, productive."], search(query, contents));
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn case_insensitive() {
</span><span class="boring"> let query = "rUsT";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.
</span><span class="boring">Trust me.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(
</span><span class="boring"> vec!["Rust:", "Trust me."],
</span><span class="boring"> search_case_insensitive(query, contents)
</span><span class="boring"> );
</span><span class="boring"> }
</span><span class="boring">}</span></code></pre>
<p><span class="caption">示例 13-19以迭代器作为参数更新 <code>Config::build</code> 的签名</span></p>
<p><code>env::args</code> 函数的标准库文档显示,它返回的迭代器的类型为 <code>std::env::Args</code>,并且这个类型实现了 <code>Iterator</code> trait 并返回 <code>String</code> 值。</p>
<p>我们已经更新了 <code>Config::build</code> 函数的签名,因此参数 <code>args</code> 有一个带有 trait bounds <code>impl Iterator&lt;Item = String&gt;</code> 的泛型类型,而不是 <code>&amp;[String]</code>。这里用到了第十章 <a href="ch10-02-traits.html#trait-%E4%BD%9C%E4%B8%BA%E5%8F%82%E6%95%B0">“trait 作为参数”</a> 部分讨论过的 <code>impl Trait</code> 语法,这意味着 <code>args</code> 可以是任何实现了 <code>Iterator</code> trait 并返回 <code>String</code>item的类型。</p>
<p>由于我们获取了 <code>args</code> 的所有权,并且将通过迭代来修改 <code>args</code>,因此我们可以在 <code>args</code> 参数的声明中添加 <code>mut</code> 关键字,使其可变。</p>
<h4 id="使用-iterator-trait-代替索引"><a class="header" href="#使用-iterator-trait-代替索引">使用 <code>Iterator</code> trait 代替索引</a></h4>
<p>接下来,我们将修改 <code>Config::build</code> 的函数体。因为 <code>args</code> 实现了 <code>Iterator</code> trait因此我们知道可以对其调用 <code>next</code> 方法!示例 13-20 更新了示例 12-23 中的代码,以使用 <code>next</code> 方法:</p>
<p><span class="filename">文件名src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">use std::env;
</span><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring"> pub ignore_case: bool,
</span><span class="boring">}
</span><span class="boring">
</span>impl Config {
pub fn build(
mut args: impl Iterator&lt;Item = String&gt;,
) -&gt; Result&lt;Config, &amp;'static str&gt; {
args.next();
let query = match args.next() {
Some(arg) =&gt; arg,
None =&gt; return Err("Didn't get a query string"),
};
let file_path = match args.next() {
Some(arg) =&gt; arg,
None =&gt; return Err("Didn't get a file path"),
};
let ignore_case = env::var("IGNORE_CASE").is_ok();
Ok(Config {
query,
file_path,
ignore_case,
})
}
}
<span class="boring">
</span><span class="boring">pub fn run(config: Config) -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
</span><span class="boring">
</span><span class="boring"> let results = if config.ignore_case {
</span><span class="boring"> search_case_insensitive(&amp;config.query, &amp;contents)
</span><span class="boring"> } else {
</span><span class="boring"> search(&amp;config.query, &amp;contents)
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> for line in results {
</span><span class="boring"> println!("{line}");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> Ok(())
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn search&lt;'a&gt;(query: &amp;str, contents: &amp;'a str) -&gt; Vec&lt;&amp;'a str&gt; {
</span><span class="boring"> let mut results = Vec::new();
</span><span class="boring">
</span><span class="boring"> for line in contents.lines() {
</span><span class="boring"> if line.contains(query) {
</span><span class="boring"> results.push(line);
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> results
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn search_case_insensitive&lt;'a&gt;(
</span><span class="boring"> query: &amp;str,
</span><span class="boring"> contents: &amp;'a str,
</span><span class="boring">) -&gt; Vec&lt;&amp;'a str&gt; {
</span><span class="boring"> let query = query.to_lowercase();
</span><span class="boring"> let mut results = Vec::new();
</span><span class="boring">
</span><span class="boring"> for line in contents.lines() {
</span><span class="boring"> if line.to_lowercase().contains(&amp;query) {
</span><span class="boring"> results.push(line);
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> results
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn case_sensitive() {
</span><span class="boring"> let query = "duct";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.
</span><span class="boring">Duct tape.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(vec!["safe, fast, productive."], search(query, contents));
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn case_insensitive() {
</span><span class="boring"> let query = "rUsT";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.
</span><span class="boring">Trust me.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(
</span><span class="boring"> vec!["Rust:", "Trust me."],
</span><span class="boring"> search_case_insensitive(query, contents)
</span><span class="boring"> );
</span><span class="boring"> }
</span><span class="boring">}</span></code></pre>
<p><span class="caption">示例 13-20修改 <code>Config::build</code> 的函数体来使用迭代器方法</span></p>
<p>请记住 <code>env::args</code> 返回值的第一个值是程序的名称。我们希望忽略它并获取下一个值,所以首先调用 <code>next</code> 且不对其返回值做任何操作。然后,我们再次调用 <code>next</code> 来获取要放入 <code>Config</code> 结构体的 <code>query</code> 字段的值。如果 <code>next</code> 返回 <code>Some</code>,使用 <code>match</code> 来提取其值。如果它返回 <code>None</code>,则意味着没有提供足够的参数并通过 <code>Err</code> 值提早返回。我们对对 <code>file_path</code> 的值也进行同样的操作。</p>
<h3 id="使用迭代器适配器来使代码更简明"><a class="header" href="#使用迭代器适配器来使代码更简明">使用迭代器适配器来使代码更简明</a></h3>
<p>I/O 项目中其他可以利用迭代器的地方是 <code>search</code> 函数,示例 13-21 中重现了第十二章结尾示例 12-19 中此函数的定义:</p>
<p><span class="filename">文件名src/lib.rs</span></p>
<pre><code class="language-rust ignore"><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Config {
</span><span class="boring"> pub fn build(args: &amp;[String]) -&gt; Result&lt;Config, &amp;'static str&gt; {
</span><span class="boring"> if args.len() &lt; 3 {
</span><span class="boring"> return Err("not enough arguments");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let query = args[1].clone();
</span><span class="boring"> let file_path = args[2].clone();
</span><span class="boring">
</span><span class="boring"> Ok(Config { query, file_path })
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn run(config: Config) -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
</span><span class="boring">
</span><span class="boring"> Ok(())
</span><span class="boring">}
</span><span class="boring">
</span>pub fn search&lt;'a&gt;(query: &amp;str, contents: &amp;'a str) -&gt; Vec&lt;&amp;'a str&gt; {
let mut results = Vec::new();
for line in contents.lines() {
if line.contains(query) {
results.push(line);
}
}
results
}
<span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn one_result() {
</span><span class="boring"> let query = "duct";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(vec!["safe, fast, productive."], search(query, contents));
</span><span class="boring"> }
</span><span class="boring">}</span></code></pre>
<p><span class="caption">示例 13-21示例 12-19 中 <code>search</code> 函数的定义</span></p>
<p>可以通过使用迭代器适配器方法来编写更简明的代码。这样做还可以避免使用一个可变的中间 <code>results</code> vector。函数式编程风格倾向于最小化可变状态的数量来使代码更简洁。去除可变状态可能会使未来的并行搜索优化变得更容易因为我们不必管理对 <code>results</code> vector 的并发访问。示例 13-22 展示了这一变化:</p>
<p><span class="filename">文件名src/lib.rs</span></p>
<pre><code class="language-rust ignore"><span class="boring">use std::env;
</span><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring"> pub ignore_case: bool,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Config {
</span><span class="boring"> pub fn build(
</span><span class="boring"> mut args: impl Iterator&lt;Item = String&gt;,
</span><span class="boring"> ) -&gt; Result&lt;Config, &amp;'static str&gt; {
</span><span class="boring"> args.next();
</span><span class="boring">
</span><span class="boring"> let query = match args.next() {
</span><span class="boring"> Some(arg) =&gt; arg,
</span><span class="boring"> None =&gt; return Err("Didn't get a query string"),
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> let file_path = match args.next() {
</span><span class="boring"> Some(arg) =&gt; arg,
</span><span class="boring"> None =&gt; return Err("Didn't get a file path"),
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> let ignore_case = env::var("IGNORE_CASE").is_ok();
</span><span class="boring">
</span><span class="boring"> Ok(Config {
</span><span class="boring"> query,
</span><span class="boring"> file_path,
</span><span class="boring"> ignore_case,
</span><span class="boring"> })
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn run(config: Config) -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
</span><span class="boring">
</span><span class="boring"> let results = if config.ignore_case {
</span><span class="boring"> search_case_insensitive(&amp;config.query, &amp;contents)
</span><span class="boring"> } else {
</span><span class="boring"> search(&amp;config.query, &amp;contents)
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> for line in results {
</span><span class="boring"> println!("{line}");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> Ok(())
</span><span class="boring">}
</span><span class="boring">
</span>pub fn search&lt;'a&gt;(query: &amp;str, contents: &amp;'a str) -&gt; Vec&lt;&amp;'a str&gt; {
contents
.lines()
.filter(|line| line.contains(query))
.collect()
}
<span class="boring">
</span><span class="boring">pub fn search_case_insensitive&lt;'a&gt;(
</span><span class="boring"> query: &amp;str,
</span><span class="boring"> contents: &amp;'a str,
</span><span class="boring">) -&gt; Vec&lt;&amp;'a str&gt; {
</span><span class="boring"> let query = query.to_lowercase();
</span><span class="boring"> let mut results = Vec::new();
</span><span class="boring">
</span><span class="boring"> for line in contents.lines() {
</span><span class="boring"> if line.to_lowercase().contains(&amp;query) {
</span><span class="boring"> results.push(line);
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> results
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn case_sensitive() {
</span><span class="boring"> let query = "duct";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.
</span><span class="boring">Duct tape.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(vec!["safe, fast, productive."], search(query, contents));
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn case_insensitive() {
</span><span class="boring"> let query = "rUsT";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.
</span><span class="boring">Trust me.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(
</span><span class="boring"> vec!["Rust:", "Trust me."],
</span><span class="boring"> search_case_insensitive(query, contents)
</span><span class="boring"> );
</span><span class="boring"> }
</span><span class="boring">}</span></code></pre>
<p><span class="caption">示例 13-22<code>search</code> 函数实现中使用迭代器适配器</span></p>
<p>回忆一下,<code>search</code> 函数的目的是返回所有 <code>contents</code> 中包含 <code>query</code> 的行。类似于示例 13-16 中的 <code>filter</code> 例子,这段代码使用 <code>filter</code> 适配器来保留 <code>line.contains(query)</code> 返回 <code>true</code> 的行。接着使用 <code>collect</code> 将匹配行收集到另一个 vector 中。这样就容易多了!尝试对 <code>search_case_insensitive</code> 函数做出同样的使用迭代器方法的修改吧。</p>
<h3 id="选择循环或迭代器"><a class="header" href="#选择循环或迭代器">选择循环或迭代器</a></h3>
<p>接下来的逻辑问题就是在代码中应该选择哪种风格,以及原因:是使用示例 13-21 中的原始实现还是使用示例 13-22 中使用迭代器的版本?大部分 Rust 程序员倾向于使用迭代器风格。开始这有点难以掌握,不过一旦你对不同迭代器的工作方式有了感觉之后,迭代器反而更容易理解。相比摆弄不同的循环并创建新 vector迭代器代码则更关注循环的高层次目的。这抽象掉那些老生常谈的代码这样就更容易看清代码所特有的概念比如迭代器中每个元素必须满足的过滤条件。</p>
<p>不过这两种实现真的完全等价吗?直觉上的假设是更底层的循环会更快一些。让我们聊聊性能吧。</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="ch13-02-iterators.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="ch13-04-performance.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="ch13-02-iterators.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="ch13-04-performance.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js"></script>
<script src="mark.min.js"></script>
<script src="searcher.js"></script>
<script src="clipboard.min.js"></script>
<script src="highlight.js"></script>
<script src="book.js"></script>
<!-- Custom JS scripts -->
<script src="ferris.js"></script>
</div>
</body>
</html>