From 0bac596862c5ac36ed4cbb284e1d5c958ea8677b Mon Sep 17 00:00:00 2001
From: Lam <15622383059@163.com>
Date: Fri, 7 Dec 2018 18:48:24 +0800
Subject: [PATCH] =?UTF-8?q?ch10-02=E8=AF=AD=E5=8F=A5=E8=B0=83=E6=95=B4+?=
=?UTF-8?q?=E9=94=99=E5=88=AB=E5=AD=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/.vuepress/dist/404.html | 17 +
src/.vuepress/dist/PREFACE.html | 17 +
src/.vuepress/dist/SUMMARY.html | 17 +
src/.vuepress/dist/appendix-00.html | 18 +
src/.vuepress/dist/appendix-01-keywords.html | 20 +
src/.vuepress/dist/appendix-02-operators.html | 18 +
.../dist/appendix-03-derivable-traits.html | 18 +
src/.vuepress/dist/appendix-04-macros.html | 123 ++++
.../dist/appendix-05-translation.html | 18 +
.../dist/appendix-06-newest-features.html | 80 +++
.../dist/appendix-07-nightly-rust.html | 17 +
.../dist/assets/css/0.styles.8614bbde.css | 1 +
.../dist/assets/img/search.83621669.svg | 1 +
src/.vuepress/dist/assets/js/10.d41a3b84.js | 1 +
src/.vuepress/dist/assets/js/100.22ca6861.js | 1 +
src/.vuepress/dist/assets/js/101.80606dd4.js | 1 +
src/.vuepress/dist/assets/js/102.75bf264b.js | 1 +
src/.vuepress/dist/assets/js/103.7615fac4.js | 1 +
src/.vuepress/dist/assets/js/104.a030e309.js | 1 +
src/.vuepress/dist/assets/js/105.ceb5b0f7.js | 1 +
src/.vuepress/dist/assets/js/106.53b25747.js | 1 +
src/.vuepress/dist/assets/js/107.a6f25383.js | 1 +
src/.vuepress/dist/assets/js/108.524846e6.js | 1 +
src/.vuepress/dist/assets/js/109.5b3e8cc8.js | 1 +
src/.vuepress/dist/assets/js/11.7f9bf643.js | 1 +
src/.vuepress/dist/assets/js/110.a106958d.js | 1 +
src/.vuepress/dist/assets/js/111.b1600e65.js | 1 +
src/.vuepress/dist/assets/js/112.81bf40f0.js | 1 +
src/.vuepress/dist/assets/js/12.c7e4d9a8.js | 1 +
src/.vuepress/dist/assets/js/13.235bc06a.js | 1 +
src/.vuepress/dist/assets/js/14.8b8a21b7.js | 1 +
src/.vuepress/dist/assets/js/15.83a607fc.js | 1 +
src/.vuepress/dist/assets/js/16.2bf746d6.js | 1 +
src/.vuepress/dist/assets/js/17.36a5d5f3.js | 1 +
src/.vuepress/dist/assets/js/18.82e00fba.js | 1 +
src/.vuepress/dist/assets/js/19.1c2116c8.js | 1 +
src/.vuepress/dist/assets/js/2.3505b190.js | 1 +
src/.vuepress/dist/assets/js/20.1f4e3d0f.js | 1 +
src/.vuepress/dist/assets/js/21.80dca6df.js | 1 +
src/.vuepress/dist/assets/js/22.24977f4c.js | 1 +
src/.vuepress/dist/assets/js/23.867b53ea.js | 1 +
src/.vuepress/dist/assets/js/24.d59d9203.js | 1 +
src/.vuepress/dist/assets/js/25.fae2b839.js | 1 +
src/.vuepress/dist/assets/js/26.ba769edc.js | 1 +
src/.vuepress/dist/assets/js/27.03216770.js | 1 +
src/.vuepress/dist/assets/js/28.82b934bf.js | 1 +
src/.vuepress/dist/assets/js/29.9bbe7e55.js | 1 +
src/.vuepress/dist/assets/js/3.ba62e17c.js | 1 +
src/.vuepress/dist/assets/js/30.ed75778f.js | 1 +
src/.vuepress/dist/assets/js/31.247a3dbb.js | 1 +
src/.vuepress/dist/assets/js/32.b1bb1e43.js | 1 +
src/.vuepress/dist/assets/js/33.3656c740.js | 1 +
src/.vuepress/dist/assets/js/34.03c1b47c.js | 1 +
src/.vuepress/dist/assets/js/35.5af5d342.js | 1 +
src/.vuepress/dist/assets/js/36.572a9496.js | 1 +
src/.vuepress/dist/assets/js/37.319db1b7.js | 1 +
src/.vuepress/dist/assets/js/38.19837e80.js | 1 +
src/.vuepress/dist/assets/js/39.72675c02.js | 1 +
src/.vuepress/dist/assets/js/4.f9a0ae28.js | 1 +
src/.vuepress/dist/assets/js/40.d9f81c69.js | 1 +
src/.vuepress/dist/assets/js/41.184751dc.js | 1 +
src/.vuepress/dist/assets/js/42.9fa6eee5.js | 1 +
src/.vuepress/dist/assets/js/43.b3333fb7.js | 1 +
src/.vuepress/dist/assets/js/44.97c13303.js | 1 +
src/.vuepress/dist/assets/js/45.8afb63d1.js | 1 +
src/.vuepress/dist/assets/js/46.ac497cb6.js | 1 +
src/.vuepress/dist/assets/js/47.dc1178aa.js | 1 +
src/.vuepress/dist/assets/js/48.50b8e29c.js | 1 +
src/.vuepress/dist/assets/js/49.385dc110.js | 1 +
src/.vuepress/dist/assets/js/5.0204093a.js | 1 +
src/.vuepress/dist/assets/js/50.cadce2ee.js | 1 +
src/.vuepress/dist/assets/js/51.d4e57e23.js | 1 +
src/.vuepress/dist/assets/js/52.323a010e.js | 1 +
src/.vuepress/dist/assets/js/53.377c0582.js | 1 +
src/.vuepress/dist/assets/js/54.8d06b2c9.js | 1 +
src/.vuepress/dist/assets/js/55.f82d7ef0.js | 1 +
src/.vuepress/dist/assets/js/56.c9db2b25.js | 1 +
src/.vuepress/dist/assets/js/57.bc11287b.js | 1 +
src/.vuepress/dist/assets/js/58.a1a44552.js | 1 +
src/.vuepress/dist/assets/js/59.4afc94b7.js | 1 +
src/.vuepress/dist/assets/js/6.4774d695.js | 1 +
src/.vuepress/dist/assets/js/60.867aed26.js | 1 +
src/.vuepress/dist/assets/js/61.74327fa7.js | 1 +
src/.vuepress/dist/assets/js/62.177c0b30.js | 1 +
src/.vuepress/dist/assets/js/63.3e0d51da.js | 1 +
src/.vuepress/dist/assets/js/64.e8778b11.js | 1 +
src/.vuepress/dist/assets/js/65.b0dde85a.js | 1 +
src/.vuepress/dist/assets/js/66.3bba1132.js | 1 +
src/.vuepress/dist/assets/js/67.fb2ec2e1.js | 1 +
src/.vuepress/dist/assets/js/68.d8ef845b.js | 1 +
src/.vuepress/dist/assets/js/69.5b80f65b.js | 1 +
src/.vuepress/dist/assets/js/7.010146f6.js | 1 +
src/.vuepress/dist/assets/js/70.e265a8a0.js | 1 +
src/.vuepress/dist/assets/js/71.08e24461.js | 1 +
src/.vuepress/dist/assets/js/72.9f1ed203.js | 1 +
src/.vuepress/dist/assets/js/73.cf073a90.js | 1 +
src/.vuepress/dist/assets/js/74.e5e6cb7a.js | 1 +
src/.vuepress/dist/assets/js/75.12d311a9.js | 1 +
src/.vuepress/dist/assets/js/76.27ceea65.js | 1 +
src/.vuepress/dist/assets/js/77.2d04e2fc.js | 1 +
src/.vuepress/dist/assets/js/78.cd7b5394.js | 1 +
src/.vuepress/dist/assets/js/79.397902f7.js | 1 +
src/.vuepress/dist/assets/js/8.de3fc0db.js | 1 +
src/.vuepress/dist/assets/js/80.8f355347.js | 1 +
src/.vuepress/dist/assets/js/81.9fcee337.js | 1 +
src/.vuepress/dist/assets/js/82.67857b15.js | 1 +
src/.vuepress/dist/assets/js/83.8638b011.js | 1 +
src/.vuepress/dist/assets/js/84.77ee6a86.js | 1 +
src/.vuepress/dist/assets/js/85.724ec824.js | 1 +
src/.vuepress/dist/assets/js/86.e0caf86b.js | 1 +
src/.vuepress/dist/assets/js/87.32cc9faa.js | 1 +
src/.vuepress/dist/assets/js/88.8db036d0.js | 1 +
src/.vuepress/dist/assets/js/89.63be6804.js | 1 +
src/.vuepress/dist/assets/js/9.a240a747.js | 1 +
src/.vuepress/dist/assets/js/90.23557c83.js | 1 +
src/.vuepress/dist/assets/js/91.0fc26ad3.js | 1 +
src/.vuepress/dist/assets/js/92.45cce200.js | 1 +
src/.vuepress/dist/assets/js/93.2a7d9b35.js | 1 +
src/.vuepress/dist/assets/js/94.25f7b020.js | 1 +
src/.vuepress/dist/assets/js/95.3d28b93b.js | 1 +
src/.vuepress/dist/assets/js/96.fb907808.js | 1 +
src/.vuepress/dist/assets/js/97.e4875071.js | 1 +
src/.vuepress/dist/assets/js/98.2859c908.js | 1 +
src/.vuepress/dist/assets/js/99.ac5b7b90.js | 1 +
src/.vuepress/dist/assets/js/app.b56e4aff.js | 8 +
src/.vuepress/dist/ch00-00-introduction.html | 19 +
.../dist/ch01-00-getting-started.html | 18 +
src/.vuepress/dist/ch01-01-installation.html | 26 +
src/.vuepress/dist/ch01-02-hello-world.html | 60 ++
src/.vuepress/dist/ch01-03-hello-cargo.html | 51 ++
.../dist/ch02-00-guessing-game-tutorial.html | 316 +++++++++++
.../ch03-00-common-programming-concepts.html | 22 +
.../ch03-01-variables-and-mutability.html | 71 +++
src/.vuepress/dist/ch03-02-data-types.html | 104 ++++
.../dist/ch03-03-how-functions-work.html | 133 +++++
src/.vuepress/dist/ch03-04-comments.html | 29 +
src/.vuepress/dist/ch03-05-control-flow.html | 187 ++++++
.../dist/ch04-00-understanding-ownership.html | 18 +
.../dist/ch04-01-what-is-ownership.html | 122 ++++
.../ch04-02-references-and-borrowing.html | 137 +++++
src/.vuepress/dist/ch04-03-slices.html | 150 +++++
src/.vuepress/dist/ch05-00-structs.html | 18 +
.../dist/ch05-01-defining-structs.html | 152 +++++
.../dist/ch05-02-example-structs.html | 94 +++
src/.vuepress/dist/ch05-03-method-syntax.html | 109 ++++
src/.vuepress/dist/ch06-00-enums.html | 18 +
.../dist/ch06-01-defining-an-enum.html | 145 +++++
src/.vuepress/dist/ch06-02-match.html | 119 ++++
src/.vuepress/dist/ch06-03-if-let.html | 64 +++
...h07-00-packages-crates-and-modules.md.html | 18 +
.../dist/ch07-01-mod-and-the-filesystem.html | 169 ++++++
...-for-making-libraries-and-executables.html | 25 +
...07-02-controlling-visibility-with-pub.html | 123 ++++
...-and-use-to-control-scope-and-privacy.html | 316 +++++++++++
.../ch07-03-importing-names-with-use.html | 130 +++++
.../dist/ch08-00-common-collections.html | 18 +
src/.vuepress/dist/ch08-01-vectors.html | 82 +++
src/.vuepress/dist/ch08-02-strings.html | 97 ++++
src/.vuepress/dist/ch08-03-hash-maps.html | 89 +++
.../dist/ch09-00-error-handling.html | 18 +
...09-01-unrecoverable-errors-with-panic.html | 80 +++
...h09-02-recoverable-errors-with-result.html | 164 ++++++
.../ch09-03-to-panic-or-not-to-panic.html | 56 ++
src/.vuepress/dist/ch10-00-generics.html | 82 +++
src/.vuepress/dist/ch10-01-syntax.html | 194 +++++++
src/.vuepress/dist/ch10-02-traits.html | 210 +++++++
.../dist/ch10-03-lifetime-syntax.html | 208 +++++++
src/.vuepress/dist/ch11-00-testing.html | 18 +
src/.vuepress/dist/ch11-01-writing-tests.html | 368 ++++++++++++
src/.vuepress/dist/ch11-02-running-tests.html | 148 +++++
.../dist/ch11-03-test-organization.html | 115 ++++
src/.vuepress/dist/ch12-00-an-io-project.html | 18 +
...2-01-accepting-command-line-arguments.html | 52 ++
.../dist/ch12-02-reading-a-file.html | 61 ++
...proving-error-handling-and-modularity.html | 208 +++++++
...04-testing-the-librarys-functionality.html | 129 +++++
...05-working-with-environment-variables.html | 145 +++++
...6-writing-to-stderr-instead-of-stdout.html | 39 ++
.../dist/ch13-00-functional-features.html | 18 +
src/.vuepress/dist/ch13-01-closures.html | 314 ++++++++++
src/.vuepress/dist/ch13-02-iterators.html | 192 +++++++
.../ch13-03-improving-our-io-project.html | 97 ++++
src/.vuepress/dist/ch13-04-performance.html | 32 ++
.../dist/ch14-00-more-about-cargo.html | 18 +
.../dist/ch14-01-release-profiles.html | 29 +
.../dist/ch14-02-publishing-to-crates-io.html | 135 +++++
.../dist/ch14-03-cargo-workspaces.html | 140 +++++
.../dist/ch14-04-installing-binaries.html | 25 +
.../dist/ch14-05-extending-cargo.html | 18 +
.../dist/ch15-00-smart-pointers.html | 18 +
src/.vuepress/dist/ch15-01-box.html | 62 ++
src/.vuepress/dist/ch15-02-deref.html | 126 ++++
src/.vuepress/dist/ch15-03-drop.html | 66 +++
src/.vuepress/dist/ch15-04-rc.html | 78 +++
.../dist/ch15-05-interior-mutability.html | 171 ++++++
.../dist/ch15-06-reference-cycles.html | 195 +++++++
src/.vuepress/dist/ch16-00-concurrency.html | 18 +
src/.vuepress/dist/ch16-01-threads.html | 170 ++++++
.../dist/ch16-02-message-passing.html | 157 +++++
src/.vuepress/dist/ch16-03-shared-state.html | 179 ++++++
...-extensible-concurrency-sync-and-send.html | 18 +
src/.vuepress/dist/ch17-00-oop.html | 18 +
src/.vuepress/dist/ch17-01-what-is-oo.html | 52 ++
src/.vuepress/dist/ch17-02-trait-objects.html | 146 +++++
.../dist/ch17-03-oo-design-patterns.html | 269 +++++++++
src/.vuepress/dist/ch18-00-patterns.html | 18 +
.../ch18-01-all-the-places-for-patterns.html | 82 +++
src/.vuepress/dist/ch18-02-refutability.html | 36 ++
.../dist/ch18-03-pattern-syntax.html | 305 ++++++++++
.../dist/ch19-00-advanced-features.html | 18 +
src/.vuepress/dist/ch19-01-unsafe-rust.html | 129 +++++
.../dist/ch19-02-advanced-lifetimes.html | 146 +++++
.../dist/ch19-03-advanced-traits.html | 280 +++++++++
.../dist/ch19-04-advanced-types.html | 105 ++++
...19-05-advanced-functions-and-closures.html | 58 ++
.../ch20-00-final-project-a-web-server.html | 18 +
.../dist/ch20-01-single-threaded.html | 205 +++++++
src/.vuepress/dist/ch20-02-multithreaded.html | 536 ++++++++++++++++++
src/.vuepress/dist/ch20-02-slow-requests.html | 44 ++
.../dist/ch20-03-designing-the-interface.html | 130 +++++
...ch20-03-graceful-shutdown-and-cleanup.html | 352 ++++++++++++
.../dist/ch20-04-storing-threads.html | 113 ++++
...ch20-05-sending-requests-via-channels.html | 286 ++++++++++
src/.vuepress/dist/foreword.html | 18 +
src/ch10-02-traits.md | 12 +-
225 files changed, 11692 insertions(+), 6 deletions(-)
create mode 100644 src/.vuepress/dist/404.html
create mode 100644 src/.vuepress/dist/PREFACE.html
create mode 100644 src/.vuepress/dist/SUMMARY.html
create mode 100644 src/.vuepress/dist/appendix-00.html
create mode 100644 src/.vuepress/dist/appendix-01-keywords.html
create mode 100644 src/.vuepress/dist/appendix-02-operators.html
create mode 100644 src/.vuepress/dist/appendix-03-derivable-traits.html
create mode 100644 src/.vuepress/dist/appendix-04-macros.html
create mode 100644 src/.vuepress/dist/appendix-05-translation.html
create mode 100644 src/.vuepress/dist/appendix-06-newest-features.html
create mode 100644 src/.vuepress/dist/appendix-07-nightly-rust.html
create mode 100644 src/.vuepress/dist/assets/css/0.styles.8614bbde.css
create mode 100644 src/.vuepress/dist/assets/img/search.83621669.svg
create mode 100644 src/.vuepress/dist/assets/js/10.d41a3b84.js
create mode 100644 src/.vuepress/dist/assets/js/100.22ca6861.js
create mode 100644 src/.vuepress/dist/assets/js/101.80606dd4.js
create mode 100644 src/.vuepress/dist/assets/js/102.75bf264b.js
create mode 100644 src/.vuepress/dist/assets/js/103.7615fac4.js
create mode 100644 src/.vuepress/dist/assets/js/104.a030e309.js
create mode 100644 src/.vuepress/dist/assets/js/105.ceb5b0f7.js
create mode 100644 src/.vuepress/dist/assets/js/106.53b25747.js
create mode 100644 src/.vuepress/dist/assets/js/107.a6f25383.js
create mode 100644 src/.vuepress/dist/assets/js/108.524846e6.js
create mode 100644 src/.vuepress/dist/assets/js/109.5b3e8cc8.js
create mode 100644 src/.vuepress/dist/assets/js/11.7f9bf643.js
create mode 100644 src/.vuepress/dist/assets/js/110.a106958d.js
create mode 100644 src/.vuepress/dist/assets/js/111.b1600e65.js
create mode 100644 src/.vuepress/dist/assets/js/112.81bf40f0.js
create mode 100644 src/.vuepress/dist/assets/js/12.c7e4d9a8.js
create mode 100644 src/.vuepress/dist/assets/js/13.235bc06a.js
create mode 100644 src/.vuepress/dist/assets/js/14.8b8a21b7.js
create mode 100644 src/.vuepress/dist/assets/js/15.83a607fc.js
create mode 100644 src/.vuepress/dist/assets/js/16.2bf746d6.js
create mode 100644 src/.vuepress/dist/assets/js/17.36a5d5f3.js
create mode 100644 src/.vuepress/dist/assets/js/18.82e00fba.js
create mode 100644 src/.vuepress/dist/assets/js/19.1c2116c8.js
create mode 100644 src/.vuepress/dist/assets/js/2.3505b190.js
create mode 100644 src/.vuepress/dist/assets/js/20.1f4e3d0f.js
create mode 100644 src/.vuepress/dist/assets/js/21.80dca6df.js
create mode 100644 src/.vuepress/dist/assets/js/22.24977f4c.js
create mode 100644 src/.vuepress/dist/assets/js/23.867b53ea.js
create mode 100644 src/.vuepress/dist/assets/js/24.d59d9203.js
create mode 100644 src/.vuepress/dist/assets/js/25.fae2b839.js
create mode 100644 src/.vuepress/dist/assets/js/26.ba769edc.js
create mode 100644 src/.vuepress/dist/assets/js/27.03216770.js
create mode 100644 src/.vuepress/dist/assets/js/28.82b934bf.js
create mode 100644 src/.vuepress/dist/assets/js/29.9bbe7e55.js
create mode 100644 src/.vuepress/dist/assets/js/3.ba62e17c.js
create mode 100644 src/.vuepress/dist/assets/js/30.ed75778f.js
create mode 100644 src/.vuepress/dist/assets/js/31.247a3dbb.js
create mode 100644 src/.vuepress/dist/assets/js/32.b1bb1e43.js
create mode 100644 src/.vuepress/dist/assets/js/33.3656c740.js
create mode 100644 src/.vuepress/dist/assets/js/34.03c1b47c.js
create mode 100644 src/.vuepress/dist/assets/js/35.5af5d342.js
create mode 100644 src/.vuepress/dist/assets/js/36.572a9496.js
create mode 100644 src/.vuepress/dist/assets/js/37.319db1b7.js
create mode 100644 src/.vuepress/dist/assets/js/38.19837e80.js
create mode 100644 src/.vuepress/dist/assets/js/39.72675c02.js
create mode 100644 src/.vuepress/dist/assets/js/4.f9a0ae28.js
create mode 100644 src/.vuepress/dist/assets/js/40.d9f81c69.js
create mode 100644 src/.vuepress/dist/assets/js/41.184751dc.js
create mode 100644 src/.vuepress/dist/assets/js/42.9fa6eee5.js
create mode 100644 src/.vuepress/dist/assets/js/43.b3333fb7.js
create mode 100644 src/.vuepress/dist/assets/js/44.97c13303.js
create mode 100644 src/.vuepress/dist/assets/js/45.8afb63d1.js
create mode 100644 src/.vuepress/dist/assets/js/46.ac497cb6.js
create mode 100644 src/.vuepress/dist/assets/js/47.dc1178aa.js
create mode 100644 src/.vuepress/dist/assets/js/48.50b8e29c.js
create mode 100644 src/.vuepress/dist/assets/js/49.385dc110.js
create mode 100644 src/.vuepress/dist/assets/js/5.0204093a.js
create mode 100644 src/.vuepress/dist/assets/js/50.cadce2ee.js
create mode 100644 src/.vuepress/dist/assets/js/51.d4e57e23.js
create mode 100644 src/.vuepress/dist/assets/js/52.323a010e.js
create mode 100644 src/.vuepress/dist/assets/js/53.377c0582.js
create mode 100644 src/.vuepress/dist/assets/js/54.8d06b2c9.js
create mode 100644 src/.vuepress/dist/assets/js/55.f82d7ef0.js
create mode 100644 src/.vuepress/dist/assets/js/56.c9db2b25.js
create mode 100644 src/.vuepress/dist/assets/js/57.bc11287b.js
create mode 100644 src/.vuepress/dist/assets/js/58.a1a44552.js
create mode 100644 src/.vuepress/dist/assets/js/59.4afc94b7.js
create mode 100644 src/.vuepress/dist/assets/js/6.4774d695.js
create mode 100644 src/.vuepress/dist/assets/js/60.867aed26.js
create mode 100644 src/.vuepress/dist/assets/js/61.74327fa7.js
create mode 100644 src/.vuepress/dist/assets/js/62.177c0b30.js
create mode 100644 src/.vuepress/dist/assets/js/63.3e0d51da.js
create mode 100644 src/.vuepress/dist/assets/js/64.e8778b11.js
create mode 100644 src/.vuepress/dist/assets/js/65.b0dde85a.js
create mode 100644 src/.vuepress/dist/assets/js/66.3bba1132.js
create mode 100644 src/.vuepress/dist/assets/js/67.fb2ec2e1.js
create mode 100644 src/.vuepress/dist/assets/js/68.d8ef845b.js
create mode 100644 src/.vuepress/dist/assets/js/69.5b80f65b.js
create mode 100644 src/.vuepress/dist/assets/js/7.010146f6.js
create mode 100644 src/.vuepress/dist/assets/js/70.e265a8a0.js
create mode 100644 src/.vuepress/dist/assets/js/71.08e24461.js
create mode 100644 src/.vuepress/dist/assets/js/72.9f1ed203.js
create mode 100644 src/.vuepress/dist/assets/js/73.cf073a90.js
create mode 100644 src/.vuepress/dist/assets/js/74.e5e6cb7a.js
create mode 100644 src/.vuepress/dist/assets/js/75.12d311a9.js
create mode 100644 src/.vuepress/dist/assets/js/76.27ceea65.js
create mode 100644 src/.vuepress/dist/assets/js/77.2d04e2fc.js
create mode 100644 src/.vuepress/dist/assets/js/78.cd7b5394.js
create mode 100644 src/.vuepress/dist/assets/js/79.397902f7.js
create mode 100644 src/.vuepress/dist/assets/js/8.de3fc0db.js
create mode 100644 src/.vuepress/dist/assets/js/80.8f355347.js
create mode 100644 src/.vuepress/dist/assets/js/81.9fcee337.js
create mode 100644 src/.vuepress/dist/assets/js/82.67857b15.js
create mode 100644 src/.vuepress/dist/assets/js/83.8638b011.js
create mode 100644 src/.vuepress/dist/assets/js/84.77ee6a86.js
create mode 100644 src/.vuepress/dist/assets/js/85.724ec824.js
create mode 100644 src/.vuepress/dist/assets/js/86.e0caf86b.js
create mode 100644 src/.vuepress/dist/assets/js/87.32cc9faa.js
create mode 100644 src/.vuepress/dist/assets/js/88.8db036d0.js
create mode 100644 src/.vuepress/dist/assets/js/89.63be6804.js
create mode 100644 src/.vuepress/dist/assets/js/9.a240a747.js
create mode 100644 src/.vuepress/dist/assets/js/90.23557c83.js
create mode 100644 src/.vuepress/dist/assets/js/91.0fc26ad3.js
create mode 100644 src/.vuepress/dist/assets/js/92.45cce200.js
create mode 100644 src/.vuepress/dist/assets/js/93.2a7d9b35.js
create mode 100644 src/.vuepress/dist/assets/js/94.25f7b020.js
create mode 100644 src/.vuepress/dist/assets/js/95.3d28b93b.js
create mode 100644 src/.vuepress/dist/assets/js/96.fb907808.js
create mode 100644 src/.vuepress/dist/assets/js/97.e4875071.js
create mode 100644 src/.vuepress/dist/assets/js/98.2859c908.js
create mode 100644 src/.vuepress/dist/assets/js/99.ac5b7b90.js
create mode 100644 src/.vuepress/dist/assets/js/app.b56e4aff.js
create mode 100644 src/.vuepress/dist/ch00-00-introduction.html
create mode 100644 src/.vuepress/dist/ch01-00-getting-started.html
create mode 100644 src/.vuepress/dist/ch01-01-installation.html
create mode 100644 src/.vuepress/dist/ch01-02-hello-world.html
create mode 100644 src/.vuepress/dist/ch01-03-hello-cargo.html
create mode 100644 src/.vuepress/dist/ch02-00-guessing-game-tutorial.html
create mode 100644 src/.vuepress/dist/ch03-00-common-programming-concepts.html
create mode 100644 src/.vuepress/dist/ch03-01-variables-and-mutability.html
create mode 100644 src/.vuepress/dist/ch03-02-data-types.html
create mode 100644 src/.vuepress/dist/ch03-03-how-functions-work.html
create mode 100644 src/.vuepress/dist/ch03-04-comments.html
create mode 100644 src/.vuepress/dist/ch03-05-control-flow.html
create mode 100644 src/.vuepress/dist/ch04-00-understanding-ownership.html
create mode 100644 src/.vuepress/dist/ch04-01-what-is-ownership.html
create mode 100644 src/.vuepress/dist/ch04-02-references-and-borrowing.html
create mode 100644 src/.vuepress/dist/ch04-03-slices.html
create mode 100644 src/.vuepress/dist/ch05-00-structs.html
create mode 100644 src/.vuepress/dist/ch05-01-defining-structs.html
create mode 100644 src/.vuepress/dist/ch05-02-example-structs.html
create mode 100644 src/.vuepress/dist/ch05-03-method-syntax.html
create mode 100644 src/.vuepress/dist/ch06-00-enums.html
create mode 100644 src/.vuepress/dist/ch06-01-defining-an-enum.html
create mode 100644 src/.vuepress/dist/ch06-02-match.html
create mode 100644 src/.vuepress/dist/ch06-03-if-let.html
create mode 100644 src/.vuepress/dist/ch07-00-packages-crates-and-modules.md.html
create mode 100644 src/.vuepress/dist/ch07-01-mod-and-the-filesystem.html
create mode 100644 src/.vuepress/dist/ch07-01-packages-and-crates-for-making-libraries-and-executables.html
create mode 100644 src/.vuepress/dist/ch07-02-controlling-visibility-with-pub.html
create mode 100644 src/.vuepress/dist/ch07-02-modules-and-use-to-control-scope-and-privacy.html
create mode 100644 src/.vuepress/dist/ch07-03-importing-names-with-use.html
create mode 100644 src/.vuepress/dist/ch08-00-common-collections.html
create mode 100644 src/.vuepress/dist/ch08-01-vectors.html
create mode 100644 src/.vuepress/dist/ch08-02-strings.html
create mode 100644 src/.vuepress/dist/ch08-03-hash-maps.html
create mode 100644 src/.vuepress/dist/ch09-00-error-handling.html
create mode 100644 src/.vuepress/dist/ch09-01-unrecoverable-errors-with-panic.html
create mode 100644 src/.vuepress/dist/ch09-02-recoverable-errors-with-result.html
create mode 100644 src/.vuepress/dist/ch09-03-to-panic-or-not-to-panic.html
create mode 100644 src/.vuepress/dist/ch10-00-generics.html
create mode 100644 src/.vuepress/dist/ch10-01-syntax.html
create mode 100644 src/.vuepress/dist/ch10-02-traits.html
create mode 100644 src/.vuepress/dist/ch10-03-lifetime-syntax.html
create mode 100644 src/.vuepress/dist/ch11-00-testing.html
create mode 100644 src/.vuepress/dist/ch11-01-writing-tests.html
create mode 100644 src/.vuepress/dist/ch11-02-running-tests.html
create mode 100644 src/.vuepress/dist/ch11-03-test-organization.html
create mode 100644 src/.vuepress/dist/ch12-00-an-io-project.html
create mode 100644 src/.vuepress/dist/ch12-01-accepting-command-line-arguments.html
create mode 100644 src/.vuepress/dist/ch12-02-reading-a-file.html
create mode 100644 src/.vuepress/dist/ch12-03-improving-error-handling-and-modularity.html
create mode 100644 src/.vuepress/dist/ch12-04-testing-the-librarys-functionality.html
create mode 100644 src/.vuepress/dist/ch12-05-working-with-environment-variables.html
create mode 100644 src/.vuepress/dist/ch12-06-writing-to-stderr-instead-of-stdout.html
create mode 100644 src/.vuepress/dist/ch13-00-functional-features.html
create mode 100644 src/.vuepress/dist/ch13-01-closures.html
create mode 100644 src/.vuepress/dist/ch13-02-iterators.html
create mode 100644 src/.vuepress/dist/ch13-03-improving-our-io-project.html
create mode 100644 src/.vuepress/dist/ch13-04-performance.html
create mode 100644 src/.vuepress/dist/ch14-00-more-about-cargo.html
create mode 100644 src/.vuepress/dist/ch14-01-release-profiles.html
create mode 100644 src/.vuepress/dist/ch14-02-publishing-to-crates-io.html
create mode 100644 src/.vuepress/dist/ch14-03-cargo-workspaces.html
create mode 100644 src/.vuepress/dist/ch14-04-installing-binaries.html
create mode 100644 src/.vuepress/dist/ch14-05-extending-cargo.html
create mode 100644 src/.vuepress/dist/ch15-00-smart-pointers.html
create mode 100644 src/.vuepress/dist/ch15-01-box.html
create mode 100644 src/.vuepress/dist/ch15-02-deref.html
create mode 100644 src/.vuepress/dist/ch15-03-drop.html
create mode 100644 src/.vuepress/dist/ch15-04-rc.html
create mode 100644 src/.vuepress/dist/ch15-05-interior-mutability.html
create mode 100644 src/.vuepress/dist/ch15-06-reference-cycles.html
create mode 100644 src/.vuepress/dist/ch16-00-concurrency.html
create mode 100644 src/.vuepress/dist/ch16-01-threads.html
create mode 100644 src/.vuepress/dist/ch16-02-message-passing.html
create mode 100644 src/.vuepress/dist/ch16-03-shared-state.html
create mode 100644 src/.vuepress/dist/ch16-04-extensible-concurrency-sync-and-send.html
create mode 100644 src/.vuepress/dist/ch17-00-oop.html
create mode 100644 src/.vuepress/dist/ch17-01-what-is-oo.html
create mode 100644 src/.vuepress/dist/ch17-02-trait-objects.html
create mode 100644 src/.vuepress/dist/ch17-03-oo-design-patterns.html
create mode 100644 src/.vuepress/dist/ch18-00-patterns.html
create mode 100644 src/.vuepress/dist/ch18-01-all-the-places-for-patterns.html
create mode 100644 src/.vuepress/dist/ch18-02-refutability.html
create mode 100644 src/.vuepress/dist/ch18-03-pattern-syntax.html
create mode 100644 src/.vuepress/dist/ch19-00-advanced-features.html
create mode 100644 src/.vuepress/dist/ch19-01-unsafe-rust.html
create mode 100644 src/.vuepress/dist/ch19-02-advanced-lifetimes.html
create mode 100644 src/.vuepress/dist/ch19-03-advanced-traits.html
create mode 100644 src/.vuepress/dist/ch19-04-advanced-types.html
create mode 100644 src/.vuepress/dist/ch19-05-advanced-functions-and-closures.html
create mode 100644 src/.vuepress/dist/ch20-00-final-project-a-web-server.html
create mode 100644 src/.vuepress/dist/ch20-01-single-threaded.html
create mode 100644 src/.vuepress/dist/ch20-02-multithreaded.html
create mode 100644 src/.vuepress/dist/ch20-02-slow-requests.html
create mode 100644 src/.vuepress/dist/ch20-03-designing-the-interface.html
create mode 100644 src/.vuepress/dist/ch20-03-graceful-shutdown-and-cleanup.html
create mode 100644 src/.vuepress/dist/ch20-04-storing-threads.html
create mode 100644 src/.vuepress/dist/ch20-05-sending-requests-via-channels.html
create mode 100644 src/.vuepress/dist/foreword.html
diff --git a/src/.vuepress/dist/404.html b/src/.vuepress/dist/404.html
new file mode 100644
index 0000000..57b42e2
--- /dev/null
+++ b/src/.vuepress/dist/404.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+ VuePress
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/PREFACE.html b/src/.vuepress/dist/PREFACE.html
new file mode 100644
index 0000000..408d7d6
--- /dev/null
+++ b/src/.vuepress/dist/PREFACE.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+ Rust 程序设计语言(第二版) 简体中文版
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/SUMMARY.html b/src/.vuepress/dist/SUMMARY.html
new file mode 100644
index 0000000..18e9258
--- /dev/null
+++ b/src/.vuepress/dist/SUMMARY.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+ Rust 程序设计语言
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/appendix-00.html b/src/.vuepress/dist/appendix-00.html
new file mode 100644
index 0000000..5a56417
--- /dev/null
+++ b/src/.vuepress/dist/appendix-00.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ 附录
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/appendix-01-keywords.html b/src/.vuepress/dist/appendix-01-keywords.html
new file mode 100644
index 0000000..eda05b8
--- /dev/null
+++ b/src/.vuepress/dist/appendix-01-keywords.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+ 附录A - 关键字
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/appendix-02-operators.html b/src/.vuepress/dist/appendix-02-operators.html
new file mode 100644
index 0000000..ba7af6b
--- /dev/null
+++ b/src/.vuepress/dist/appendix-02-operators.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ 附录B - 运算符与符号
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/appendix-03-derivable-traits.html b/src/.vuepress/dist/appendix-03-derivable-traits.html
new file mode 100644
index 0000000..a0940f1
--- /dev/null
+++ b/src/.vuepress/dist/appendix-03-derivable-traits.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ 附录C - 可派生的 trait
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/appendix-04-macros.html b/src/.vuepress/dist/appendix-04-macros.html
new file mode 100644
index 0000000..89257e2
--- /dev/null
+++ b/src/.vuepress/dist/appendix-04-macros.html
@@ -0,0 +1,123 @@
+
+
+
+
+
+ 附录D - 宏
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/appendix-05-translation.html b/src/.vuepress/dist/appendix-05-translation.html
new file mode 100644
index 0000000..e6c43f1
--- /dev/null
+++ b/src/.vuepress/dist/appendix-05-translation.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ 附录E - 本书翻译
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/appendix-06-newest-features.html b/src/.vuepress/dist/appendix-06-newest-features.html
new file mode 100644
index 0000000..3877296
--- /dev/null
+++ b/src/.vuepress/dist/appendix-06-newest-features.html
@@ -0,0 +1,80 @@
+
+
+
+
+
+ F - 最新功能
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/appendix-07-nightly-rust.html b/src/.vuepress/dist/appendix-07-nightly-rust.html
new file mode 100644
index 0000000..856c72a
--- /dev/null
+++ b/src/.vuepress/dist/appendix-07-nightly-rust.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+ G - Rust 是如何开发的与 “Nightly Rust”
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/dist/assets/css/0.styles.8614bbde.css b/src/.vuepress/dist/assets/css/0.styles.8614bbde.css
new file mode 100644
index 0000000..9a633c9
--- /dev/null
+++ b/src/.vuepress/dist/assets/css/0.styles.8614bbde.css
@@ -0,0 +1 @@
+.home{padding:3.6rem 2rem 0;max-width:960px;margin:0 auto}.home .hero{text-align:center}.home .hero img{max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:#6a8bad}.home .hero .action-button{display:inline-block;font-size:1.2rem;color:#fff;background-color:#3eaf7c;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #389d70}.home .hero .action-button:hover{background-color:#4abf8a}.home .features{border-top:1px solid #eaecef;padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:#3a5169}.home .feature p{color:#4e6e8e}.home .footer{padding:2.5rem;border-top:1px solid #eaecef;text-align:center;color:#4e6e8e}@media (max-width:719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.badge[data-v-c23f6ad2]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff;margin-right:5px;background-color:#42b983}.badge.middle[data-v-c23f6ad2]{vertical-align:middle}.badge.top[data-v-c23f6ad2]{vertical-align:top}.badge.green[data-v-c23f6ad2],.badge.tip[data-v-c23f6ad2]{background-color:#42b983}.badge.error[data-v-c23f6ad2]{background-color:#da5961}.badge.warn[data-v-c23f6ad2],.badge.warning[data-v-c23f6ad2],.badge.yellow[data-v-c23f6ad2]{background-color:#e7c000}.search-box{display:inline-block;position:relative;margin-right:1rem}.search-box input{cursor:text;width:10rem;color:#4e6e8e;display:inline-block;border:1px solid #cfd4db;border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all .2s ease;background:#fff url(/assets/img/search.83621669.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#3eaf7c}.search-box .suggestions{background:#fff;width:20rem;position:absolute;top:1.5rem;border:1px solid #cfd4db;border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestions.align-right{right:0}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:#5d82a6}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused{background-color:#f3f4f5}.search-box .suggestion.focused a{color:#3eaf7c}@media (max-width:959px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width:959px) and (min-width:719px){.search-box .suggestions{left:0}}@media (max-width:719px){.search-box{margin-right:0}.search-box input{left:1rem}.search-box .suggestions{right:0}}@media (max-width:419px){.search-box .suggestions{width:calc(100vw - 4rem)}.search-box input:focus{width:8rem}}.dropdown-enter,.dropdown-leave-to{height:0!important}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .dropdown-title{display:block}.dropdown-wrapper .dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item h4{margin:.45rem 0 0;border-top:1px solid #eee;padding:.45rem 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover{color:#3eaf7c}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid #3eaf7c;border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4{margin-top:0;padding-top:0;border-top:0}@media (max-width:719px){.dropdown-wrapper.open .dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item h4{border-top:0;margin-top:0;padding-top:0}.dropdown-wrapper .nav-dropdown .dropdown-item>a,.dropdown-wrapper .nav-dropdown .dropdown-item h4{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:719px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper .dropdown-title .arrow{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid #ccc;border-bottom:0}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:#fff;padding:.6rem 0;border:1px solid #ddd;border-bottom-color:#ccc;text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.nav-links{display:inline-block}.nav-links a{line-height:1.4rem;color:inherit}.nav-links a.router-link-active,.nav-links a:hover{color:#3eaf7c}.nav-links .nav-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:2rem}.nav-links .nav-item:first-child{margin-left:0}.nav-links .repo-link{margin-left:1.5rem}@media (max-width:719px){.nav-links .nav-item,.nav-links .repo-link{margin-left:0}}@media (min-width:719px){.nav-links a.router-link-active,.nav-links a:hover{color:#2c3e50}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid #46bd87}}.navbar{padding:.7rem 1.5rem;line-height:2.2rem;position:relative}.navbar a,.navbar img,.navbar span{display:inline-block}.navbar .logo{height:2.2rem;min-width:2.2rem;margin-right:.8rem;vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:#2c3e50;position:relative}.navbar .links{padding-left:1.5rem;box-sizing:border-box;background-color:#fff;white-space:nowrap;font-size:.9rem;position:absolute;right:1.5rem;top:.7rem;display:flex}.navbar .links .search-box{flex:0 0 auto;vertical-align:top}.navbar .links .nav-links{flex:1}@media (max-width:719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}.navbar .links{padding-left:1.5rem}}.page-edit,.page-nav{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-edit,.page-nav{padding:2rem}}@media (max-width:419px){.page-edit,.page-nav{padding:1.5rem}}.page{padding-bottom:2rem}.page-edit{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block}.page-edit .edit-link a{color:#4e6e8e;margin-right:.25rem}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:#4e6e8e}.page-edit .last-updated .time{font-weight:400;color:#aaa}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid #eaecef;padding-top:1rem;overflow:auto}.page-nav .next{float:right}@media (max-width:719px){.page-edit .edit-link{margin-bottom:.5rem}.page-edit .last-updated{font-size:.8em;float:none;text-align:left}}.sidebar-button{display:none;width:1.25rem;height:1.25rem;position:absolute;padding:.6rem;top:.6rem;left:1rem}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (max-width:719px){.sidebar-button{display:block}}.sidebar-group:not(.first){margin-top:1em}.sidebar-group .sidebar-group{padding-left:.5em}.sidebar-group:not(.collapsable) .sidebar-heading{cursor:auto;color:inherit}.sidebar-heading{color:#999;transition:color .15s ease;cursor:pointer;font-size:1.1em;font-weight:700;padding:0 1.5rem;margin-top:0;margin-bottom:.5rem}.sidebar-heading.open,.sidebar-heading:hover{color:inherit}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-heading:.open .arrow{top:-.18em}.sidebar-group-items{transition:height .1s ease-out;overflow:hidden}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .nav-links{display:none;border-bottom:1px solid #eaecef;padding:.5rem 0 .75rem 0}.sidebar .nav-links a{font-weight:600}.sidebar .nav-links .nav-item,.sidebar .nav-links .repo-link{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-links{padding:1.5rem 0}@media (max-width:719px){.sidebar .nav-links{display:block}.sidebar .nav-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-links{padding:1rem 0}}.sw-update-popup{position:fixed;right:1em;bottom:1em;padding:1em;border:1px solid #3eaf7c;border-radius:3px;background:#fff;box-shadow:0 4px 16px rgba(0,0,0,.5);text-align:center}.sw-update-popup button{margin-top:.5em;padding:.25em 2em}.sw-update-popup-enter-active,.sw-update-popup-leave-active{transition:opacity .3s,transform .3s}.sw-update-popup-enter,.sw-update-popup-leave-to{opacity:0;transform:translateY(50%) scale(.5)}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}#nprogress{pointer-events:none}#nprogress .bar{background:#3eaf7c;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #3eaf7c,0 0 5px #3eaf7c;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#3eaf7c;border-left-color:#3eaf7c;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.content code{color:#476582;padding:.25rem .5rem;margin:0;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.content pre,.content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;background-color:#282c34;border-radius:6px;overflow:auto}.content pre[class*=language-] code,.content pre code{color:#fff;padding:0;background-color:transparent;border-radius:0}div[class*=language-]{position:relative;background-color:#282c34;border-radius:6px}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlighted{background-color:rgba(0,0,0,.66)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent;position:relative;z-index:1}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:hsla(0,0%,100%,.4)}div[class*=language-]:not(.line-numbers-mode) .line-numbers-wrapper{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlighted{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlighted:before{content:" ";position:absolute;z-index:3;left:0;top:0;display:block;width:3.5rem;height:100%;background-color:rgba(0,0,0,.66)}div[class*=language-].line-numbers-mode pre{padding-left:4.5rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers-wrapper{position:absolute;top:0;width:3.5rem;text-align:center;color:hsla(0,0%,100%,.3);padding:1.25rem 0;line-height:1.4}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number,div[class*=language-].line-numbers-mode .line-numbers-wrapper br{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number{position:relative;z-index:4;font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;z-index:2;top:0;left:0;width:3.5rem;height:100%;border-radius:6px 0 0 6px;border-right:1px solid rgba(0,0,0,.66);background-color:#282c34}div[class~=language-js]:before{content:"js"}div[class~=language-ts]:before{content:"ts"}div[class~=language-html]:before{content:"html"}div[class~=language-md]:before{content:"md"}div[class~=language-vue]:before{content:"vue"}div[class~=language-css]:before{content:"css"}div[class~=language-sass]:before{content:"sass"}div[class~=language-scss]:before{content:"scss"}div[class~=language-less]:before{content:"less"}div[class~=language-stylus]:before{content:"stylus"}div[class~=language-go]:before{content:"go"}div[class~=language-java]:before{content:"java"}div[class~=language-c]:before{content:"c"}div[class~=language-sh]:before{content:"sh"}div[class~=language-yaml]:before{content:"yaml"}div[class~=language-py]:before{content:"py"}div[class~=language-javascript]:before{content:"js"}div[class~=language-typescript]:before{content:"ts"}div[class~=language-markup]:before{content:"html"}div[class~=language-markdown]:before{content:"md"}div[class~=language-json]:before{content:"json"}div[class~=language-ruby]:before{content:"rb"}div[class~=language-python]:before{content:"py"}div[class~=language-bash]:before{content:"sh"}.custom-block .custom-block-title{font-weight:600;margin-bottom:-.4rem}.custom-block.danger,.custom-block.tip,.custom-block.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#f3f5f7;border-color:#42b983}.custom-block.warning{background-color:rgba(255,229,100,.3);border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:#2c3e50}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:#2c3e50}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-bottom:6px solid #ccc}.arrow.down,.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent}.arrow.down{border-top:6px solid #ccc}.arrow.right{border-left:6px solid #ccc}.arrow.left,.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent}.arrow.left{border-right:6px solid #ccc}.content:not(.custom){max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.content:not(.custom){padding:2rem}}@media (max-width:419px){.content:not(.custom){padding:1.5rem}}.table-of-contents .badge{vertical-align:middle}body,html{padding:0;margin:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px;color:#2c3e50}.page{padding-left:20rem}.navbar{z-index:20;right:0;height:3.6rem;background-color:#fff;box-sizing:border-box;border-bottom:1px solid #eaecef}.navbar,.sidebar-mask{position:fixed;top:0;left:0}.sidebar-mask{z-index:9;width:100vw;height:100vh;display:none}.sidebar{font-size:15px;background-color:#fff;width:20rem;position:fixed;z-index:10;margin:0;top:3.6rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid #eaecef;overflow-y:auto}.content:not(.custom)>:first-child{margin-top:3.6rem}.content:not(.custom) a:hover{text-decoration:underline}.content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.content:not(.custom) img{max-width:100%}.content.custom{padding:0;margin:0}.content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#3eaf7c}p a code{font-weight:400}kbd{background:#eee;border:.15rem solid #ddd;border-bottom:.25rem solid #ddd;border-radius:.15rem;padding:0 .15em}blockquote{font-size:1.2rem;color:#999;border-left:.25rem solid #dfe2e5;margin-left:0;padding-left:1rem}ol,ul{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}.content:not(.custom)>h1,.content:not(.custom)>h2,.content:not(.custom)>h3,.content:not(.custom)>h4,.content:not(.custom)>h5,.content:not(.custom)>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.content:not(.custom)>h1:first-child,.content:not(.custom)>h2:first-child,.content:not(.custom)>h3:first-child,.content:not(.custom)>h4:first-child,.content:not(.custom)>h5:first-child,.content:not(.custom)>h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.content:not(.custom)>h1:first-child+.custom-block,.content:not(.custom)>h1:first-child+p,.content:not(.custom)>h1:first-child+pre,.content:not(.custom)>h2:first-child+.custom-block,.content:not(.custom)>h2:first-child+p,.content:not(.custom)>h2:first-child+pre,.content:not(.custom)>h3:first-child+.custom-block,.content:not(.custom)>h3:first-child+p,.content:not(.custom)>h3:first-child+pre,.content:not(.custom)>h4:first-child+.custom-block,.content:not(.custom)>h4:first-child+p,.content:not(.custom)>h4:first-child+pre,.content:not(.custom)>h5:first-child+.custom-block,.content:not(.custom)>h5:first-child+p,.content:not(.custom)>h5:first-child+pre,.content:not(.custom)>h6:first-child+.custom-block,.content:not(.custom)>h6:first-child+p,.content:not(.custom)>h6:first-child+pre{margin-top:2rem}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid #eaecef}h3{font-size:1.35rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0}a.header-anchor:hover{text-decoration:none}.line-number,code,kbd{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}ol,p,ul{line-height:1.7}hr{border:0;border-top:1px solid #eaecef}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto}tr{border-top:1px solid #dfe2e5}tr:nth-child(2n){background-color:#f6f8fa}td,th{border:1px solid #dfe2e5;padding:.6em 1em}.custom-layout{padding-top:3.6rem}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-navbar .custom-layout{padding-top:0}@media (min-width:720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}@media (max-width:959px){.sidebar{font-size:15px;width:16.4rem}.page{padding-left:16.4rem}}@media (max-width:719px){.sidebar{top:0;padding-top:3.6rem;transform:translateX(-100%);transition:transform .2s ease}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.icon.outbound{color:#aaa;display:inline-block}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}a.sidebar-link{font-weight:400;display:inline-block;color:#2c3e50;border-left:.25rem solid transparent;padding:.35rem 1rem .35rem 1.25rem;line-height:1.4;width:100%;box-sizing:border-box}a.sidebar-link:hover{color:#3eaf7c}a.sidebar-link.active{font-weight:600;color:#3eaf7c;border-left-color:#3eaf7c}.sidebar-group a.sidebar-link{padding-left:2rem}.sidebar-sub-headers a.sidebar-link{padding-top:.25rem;padding-bottom:.25rem;border-left:none}.sidebar-sub-headers a.sidebar-link.active{font-weight:500}
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/img/search.83621669.svg b/src/.vuepress/dist/assets/img/search.83621669.svg
new file mode 100644
index 0000000..03d8391
--- /dev/null
+++ b/src/.vuepress/dist/assets/img/search.83621669.svg
@@ -0,0 +1 @@
+
diff --git a/src/.vuepress/dist/assets/js/10.d41a3b84.js b/src/.vuepress/dist/assets/js/10.d41a3b84.js
new file mode 100644
index 0000000..805f606
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/10.d41a3b84.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[10],{269:function(r,t,e){"use strict";e.r(t);var n=e(0),o=Object(n.a)({},function(){var r=this,t=r.$createElement,e=r._self._c||t;return e("div",{staticClass:"content"},[r._m(0),r._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/appendix-05-translation.md",target:"_blank",rel:"noopener noreferrer"}},[r._v("appendix-05-translation.md"),e("OutboundLink")],1),r._v(" "),e("br"),r._v("\ncommit 9c3756a33e6c1aa07a762ffa853fcdfcffb48ddc")])]),r._v(" "),e("p",[r._v("一些非英语语言的资源。多数仍在翻译中;查阅"),e("a",{attrs:{href:"https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3ATranslations",target:"_blank",rel:"noopener noreferrer"}},[r._v("翻译标签"),e("OutboundLink")],1),r._v("来帮助我们或使我们知道新的翻译!")]),r._v(" "),e("ul",[e("li",[e("a",{attrs:{href:"https://github.com/rust-br/rust-book-pt-br",target:"_blank",rel:"noopener noreferrer"}},[r._v("Português"),e("OutboundLink")],1),r._v(" (BR)")]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/nunojesus/rust-book-pt-pt",target:"_blank",rel:"noopener noreferrer"}},[r._v("Português"),e("OutboundLink")],1),r._v(" (PT)")]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/hngnaig/rust-lang-book/tree/vi-VN",target:"_blank",rel:"noopener noreferrer"}},[r._v("Tiếng việt"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/KaiserY/trpl-zh-cn",target:"_blank",rel:"noopener noreferrer"}},[r._v("简体中文"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/pavloslav/rust-book-uk-ua",target:"_blank",rel:"noopener noreferrer"}},[r._v("Українська"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/thecodix/book",target:"_blank",rel:"noopener noreferrer"}},[r._v("Español"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/AgeOfWar/rust-book-it",target:"_blank",rel:"noopener noreferrer"}},[r._v("Italiano"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/iDeBugger/rust-book-ru",target:"_blank",rel:"noopener noreferrer"}},[r._v("Русский"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/rinthel/rust-lang-book-ko",target:"_blank",rel:"noopener noreferrer"}},[r._v("한국어"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/hazama-yuinyan/book",target:"_blank",rel:"noopener noreferrer"}},[r._v("日本語"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/quadrifoglio/rust-book-fr",target:"_blank",rel:"noopener noreferrer"}},[r._v("Français"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/paytchoo/book-pl",target:"_blank",rel:"noopener noreferrer"}},[r._v("Polski"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/idanmel/rust-book-heb",target:"_blank",rel:"noopener noreferrer"}},[r._v("עברית"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/agentzero1/book",target:"_blank",rel:"noopener noreferrer"}},[r._v("Cebuano"),e("OutboundLink")],1)]),r._v(" "),e("li",[e("a",{attrs:{href:"https://github.com/josephace135/book",target:"_blank",rel:"noopener noreferrer"}},[r._v("Tagalog"),e("OutboundLink")],1)])])])},[function(){var r=this.$createElement,t=this._self._c||r;return t("h2",{attrs:{id:"附录e-本书翻译"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#附录e-本书翻译","aria-hidden":"true"}},[this._v("#")]),this._v(" 附录E - 本书翻译")])}],!1,null,null,null);o.options.__file="appendix-05-translation.md";t.default=o.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/100.22ca6861.js b/src/.vuepress/dist/assets/js/100.22ca6861.js
new file mode 100644
index 0000000..b548ca4
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/100.22ca6861.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[100],{170:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch19-03-advanced-traits.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch19-03-advanced-traits.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit 9d5b9a573daf5fa0c98b3a3005badcea4a0a5211")])]),t._v(" "),a("p",[t._v("第十章 “trait:定义共享的行为” 部分,我们第一次涉及到了 trait,不过就像生命周期一样,我们并没有覆盖一些较为高级的细节。现在我们更加了解 Rust 了,可以深入理解其本质了。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),a("p",[t._v("本章所描述的大部分内容都非常少见。关联类型则比较适中;它们比本书其他的内容要少见,不过比本章中的很多内容要更常见。")]),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),a("p",[t._v("这可能看起来像一个类似泛型的概念,因为它允许定义一个函数而不指定其可以处理的类型。那么为什么要使用关联类型呢?")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),a("p",[t._v("默认参数类型主要用于如下两个方面:")]),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),a("p",[t._v("第一个目的是相似的,但过程是反过来的:如果需要为现有 trait 增加类型参数,为其提供一个默认类型将允许我们在不破坏现有实现代码的基础上扩展 trait 的功能。")]),t._v(" "),t._m(35),t._v(" "),a("p",[t._v("Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法,也不能阻止为同一类型同时实现这两个 trait。甚至直接在类型上实现开始已经有的同名方法也是可能的!")]),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._m(49),t._v(" "),t._m(50),t._v(" "),a("p",[t._v("运行这段代码会打印出:")]),t._v(" "),t._m(51),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),t._m(56),t._v(" "),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._m(60),t._v(" "),t._m(61),t._v(" "),t._m(62),t._m(63),t._v(" "),t._m(64),t._v(" "),t._m(65),t._m(66),t._v(" "),t._m(67),t._v(" "),t._m(68),t._m(69),t._v(" "),t._m(70),t._v(" "),t._m(71),a("p",[t._v("通常,完全限定语法定义为:")]),t._v(" "),t._m(72),t._m(73),t._v(" "),t._m(74),t._v(" "),t._m(75),t._v(" "),t._m(76),t._v(" "),t._m(77),t._m(78),t._v(" "),t._m(79),t._v(" "),t._m(80),t._m(81),t._v(" "),t._m(82),t._v(" "),t._m(83),t._v(" "),t._m(84),t._v(" "),t._m(85),t._m(86),t._v(" "),t._m(87),t._m(88),t._v(" "),t._m(89),t._v(" "),t._m(90),t._m(91),t._v(" "),t._m(92),t._v(" "),t._m(93),t._v(" "),t._m(94),t._v(" "),t._m(95),t._v(" "),t._m(96),t._v(" "),t._m(97),t._m(98),t._v(" "),t._m(99),t._v(" "),t._m(100),t._v(" "),a("p",[t._v("上面便是 newtype 模式如何与 trait 结合使用的;还有一个不涉及 trait 的实用模式。现在让我们将话题的焦点转移到一些与 Rust 类型系统交互的高级方法上来吧。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"高级-trait"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#高级-trait","aria-hidden":"true"}},[this._v("#")]),this._v(" 高级 trait")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"关联类型在-trait-定义中指定占位符类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#关联类型在-trait-定义中指定占位符类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 关联类型在 trait 定义中指定占位符类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("关联类型")]),this._v("("),s("em",[this._v("associated types")]),this._v(")是一个将类型占位符与 trait 相关联的方式,这样 trait 的方法签名中就可以使用这些占位符类型。trait 的实现者会针对特定的实现在这个类型的位置指定相应的具体类型。如此可以定义一个使用多种类型的 trait,直到实现此 trait 时都无需知道这些类型具体是什么。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("一个带有关联类型的 trait 的例子是标准库提供的 "),a("code",[t._v("Iterator")]),t._v(" trait。它有一个叫做 "),a("code",[t._v("Item")]),t._v(" 的关联类型来替代遍历的值的类型。第十三章的 “"),a("code",[t._v("Iterator")]),t._v(" trait 和 "),a("code",[t._v("next")]),t._v(" 方法” 部分曾提到过 "),a("code",[t._v("Iterator")]),t._v(" trait 的定义如示例 19-20 所示:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Iterator "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Item"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("next")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Option"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Self"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Item"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-20: "),s("code",[this._v("Iterator")]),this._v(" trait 的定义中带有关联类型 "),s("code",[this._v("Item")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("Iterator")]),t._v(" trait 有一个关联类型 "),a("code",[t._v("Item")]),t._v("。"),a("code",[t._v("Item")]),t._v(" 是一个占位类型,同时 "),a("code",[t._v("next")]),t._v(" 方法会返回 "),a("code",[t._v("Option")]),t._v(" 类型的值。这个 trait 的实现者会指定 "),a("code",[t._v("Item")]),t._v(" 的具体类型,然而不管实现者指定何种类型, "),a("code",[t._v("next")]),t._v(" 方法都会返回一个包含了此具体类型值的 "),a("code",[t._v("Option")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"关联类型-vs-泛型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#关联类型-vs-泛型","aria-hidden":"true"}},[this._v("#")]),this._v(" 关联类型 vs 泛型")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("让我们通过一个在第十三章中出现的 "),a("code",[t._v("Counter")]),t._v(" 结构体上实现 "),a("code",[t._v("Iterator")]),t._v(" trait 的例子来检视其中的区别。在示例 13-21 中,指定了 "),a("code",[t._v("Item")]),t._v(" 的类型为 "),a("code",[t._v("u32")]),t._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("impl Iterator for Counter {\n type Item = u32;\n\n fn next(&mut self) -> Option {\n // --snip--\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这类似于泛型。那么为什么 "),s("code",[this._v("Iterator")]),this._v(" trait 不像示例 19-21 那样定义呢?")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Iterator"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("next")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Option"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-21: 一个使用泛型的 "),s("code",[this._v("Iterator")]),this._v(" trait 假象定义")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("区别在于当如示例 19-21 那样使用泛型时,则不得不在每一个实现中标注类型。这是因为我们也可以实现为 "),a("code",[t._v("Iterator for Counter")]),t._v(",或任何其他类型,这样就可以有多个 "),a("code",[t._v("Counter")]),t._v(" 的 "),a("code",[t._v("Iterator")]),t._v(" 的实现。换句话说,当 trait 有泛型参数时,可以多次实现这个 trait,每次需改变泛型参数的具体类型。接着当使用 "),a("code",[t._v("Counter")]),t._v(" 的 "),a("code",[t._v("next")]),t._v(" 方法时,必须提供类型注解来表明希望使用 "),a("code",[t._v("Iterator")]),t._v(" 的哪一个实现。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("通过关联类型,则无需标注类型因为不能多次实现这个 trait。对于示例 19-20,我们只能选择一次 "),a("code",[t._v("Item")]),t._v(" 会是什么类型,因为只能有一个 "),a("code",[t._v("impl Iterator for Counter")]),t._v("。当调用 "),a("code",[t._v("Counter")]),t._v(" 的 "),a("code",[t._v("next")]),t._v(" 时不必每次指定我们需要 "),a("code",[t._v("u32")]),t._v(" 值的迭代器。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"默认泛型类型参数和运算符重载"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#默认泛型类型参数和运算符重载","aria-hidden":"true"}},[this._v("#")]),this._v(" 默认泛型类型参数和运算符重载")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当使用泛型类型参数时,可以为泛型指定一个默认的具体类型。如果默认类型就足够的话,这消除了为具体类型实现 trait 的需要。为泛型类型指定默认类型的语法是在声明泛型类型时使用 "),s("code",[this._v("")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这种情况的一个非常好的例子是用于运算符重载。运算符重载是指在特定情况下自定义运算符(比如 "),s("code",[this._v("+")]),this._v(")行为的操作。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("Rust 并不允许创建自定义运算符或重载任意运算符,不过 "),a("code",[t._v("std::ops")]),t._v(" 中所列出的运算符和相应的 trait "),a("strong",[t._v("可以")]),t._v(" 通过实现运算符相关 trait 来重载。例如,示例 19-22 中展示了如何在 "),a("code",[t._v("Point")]),t._v(" 结构体上实现 "),a("code",[t._v("Add")]),t._v(" trait 来重载 "),a("code",[t._v("+")]),t._v(" 运算符,这样就可以将两个 "),a("code",[t._v("Point")]),t._v(" 实例相加了:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("ops"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Add"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug,PartialEq)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Add "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Output "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("add")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Point"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("y "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("y"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-22: 实现 "),s("code",[this._v("Add")]),this._v(" trait 重载 "),s("code",[this._v("Point")]),this._v(" 实例的 "),s("code",[this._v("+")]),this._v(" 运算符")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("add")]),t._v(" 方法将两个 "),a("code",[t._v("Point")]),t._v(" 实例的 "),a("code",[t._v("x")]),t._v(" 值和 "),a("code",[t._v("y")]),t._v(" 值分别相加来创建一个新的 "),a("code",[t._v("Point")]),t._v("。"),a("code",[t._v("Add")]),t._v(" trait 有一个叫做 "),a("code",[t._v("Output")]),t._v(" 的关联类型,它用来决定 "),a("code",[t._v("add")]),t._v(" 方法的返回值类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里默认泛型类型位于 "),s("code",[this._v("Add")]),this._v(" trait 中。这里是其定义:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Add"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("RHS"),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v("Self"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Output"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("add")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" rhs"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" RHS"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Self"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Output"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这看来应该很熟悉,这是一个带有一个方法和一个关联类型的 trait。比较陌生的部分是尖括号中的 "),a("code",[t._v("RHS=Self")]),t._v(":这个语法叫做 "),a("strong",[t._v("默认类型参数")]),t._v("("),a("em",[t._v("default type parameters")]),t._v(")。"),a("code",[t._v("RHS")]),t._v(" 是一个泛型类型参数 —— “right hand side” 的缩写 —— 它用于 "),a("code",[t._v("add")]),t._v(" 方法中的 "),a("code",[t._v("rhs")]),t._v(" 参数。如果实现 "),a("code",[t._v("Add")]),t._v(" trait 时不指定 "),a("code",[t._v("RHS")]),t._v(" 的具体类型,"),a("code",[t._v("RHS")]),t._v(" 的类型将是默认的 "),a("code",[t._v("Self")]),t._v(" 类型,也就是在其上实现 "),a("code",[t._v("Add")]),t._v(" 的类型。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("当为 "),a("code",[t._v("Point")]),t._v(" 实现 "),a("code",[t._v("Add")]),t._v(" 时,使用了默认的 "),a("code",[t._v("RHS")]),t._v(",因为我们希望将两个 "),a("code",[t._v("Point")]),t._v(" 实例相加。让我们看看一个实现 "),a("code",[t._v("Add")]),t._v(" trait 时希望自定义 "),a("code",[t._v("RHS")]),t._v(" 类型而不是使用默认类型的例子")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这里有两个存放不同单元值的结构体,"),a("code",[t._v("Millimeters")]),t._v(" 和 "),a("code",[t._v("Meters")]),t._v("。我们希望能够将毫米值与米值相加,并让 "),a("code",[t._v("Add")]),t._v(" 的实现正确处理转换。可以为 "),a("code",[t._v("Millimeters")]),t._v(" 实现 "),a("code",[t._v("Add")]),t._v(" 并以 "),a("code",[t._v("Meters")]),t._v(" 作为右手边,如示例 19-23 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("ops"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Add"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("Millimeters")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("Meters")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Add"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Meters"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Millimeters "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Output "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Millimeters"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("add")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Meters"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Millimeters "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("Millimeters")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("other"),a("span",{attrs:{class:"token number"}},[t._v(".0")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1000")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("span",{staticClass:"caption"},[t._v("示例 19-23: 在 "),a("code",[t._v("Millimeters")]),t._v(" 上实现 "),a("code",[t._v("Add")]),t._v(",以便能够将 "),a("code",[t._v("Millimeters")]),t._v(" 与 "),a("code",[t._v("Meters")]),t._v(" 相加")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("为了使 "),a("code",[t._v("Millimeters")]),t._v(" 和 "),a("code",[t._v("Meters")]),t._v(" 能够相加,我们指定 "),a("code",[t._v("impl Add")]),t._v(" 来设定 "),a("code",[t._v("RHS")]),t._v(" 类型参数的值而不是使用默认的 "),a("code",[t._v("Self")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ol",[s("li",[this._v("扩展类型而不破坏现有代码。")]),this._v(" "),s("li",[this._v("在大部分用户都不需要的特定情况进行自定义。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("标准库的 "),s("code",[this._v("Add")]),this._v(" trait 就是一个第二个目的例子:大部分时候你会将两个相似的类型相加,不过它提供了自定义额外行为的能力。在 "),s("code",[this._v("Add")]),this._v(" trait 定义中使用默认类型参数意味着大部分时候无需指定额外的参数。换句话说,一小部分实现的样板代码是不必要的,这样使用 trait 就更容易了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"完全限定语法与消歧义:调用相同名称的方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#完全限定语法与消歧义:调用相同名称的方法","aria-hidden":"true"}},[this._v("#")]),this._v(" 完全限定语法与消歧义:调用相同名称的方法")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("不过,当调用这些同名方法时,需要告诉 Rust 我们希望使用哪一个。考虑一下示例 19-24 中的代码,这里定义了 trait "),a("code",[t._v("Pilot")]),t._v(" 和 "),a("code",[t._v("Wizard")]),t._v(" 都拥有方法 "),a("code",[t._v("fly")]),t._v("。接着在一个本身已经实现了名为 "),a("code",[t._v("fly")]),t._v(" 方法的类型 "),a("code",[t._v("Human")]),t._v(" 上实现这两个 trait。每一个 "),a("code",[t._v("fly")]),t._v(" 方法都进行了不同的操作:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("甚至也可以直接在类型上实现相同名称的方法!那么为了能使用相同的名称调用每一个方法,需要告诉 Rust 我们希望使用哪个方法。考虑一下列表 19-27 中的代码,trait "),a("code",[t._v("Foo")]),t._v(" 和 "),a("code",[t._v("Bar")]),t._v(" 都拥有方法 "),a("code",[t._v("f")]),t._v(",并在结构体 "),a("code",[t._v("Baz")]),t._v(" 上实现了这两个 trait,结构体也有一个叫做 "),a("code",[t._v("f")]),t._v(" 的方法:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Pilot "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Wizard "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Human"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Pilot "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Human "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"This is your captain speaking."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Wizard "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Human "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Up!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Human "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"*waving arms furiously*"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-24: 两个 trait 定义为拥有 "),s("code",[this._v("fly")]),this._v(" 方法,并在直接定义有 "),s("code",[this._v("fly")]),this._v(" 方法的 "),s("code",[this._v("Human")]),this._v(" 类型上实现这两个 trait")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当调用 "),s("code",[this._v("Human")]),this._v(" 实例的 "),s("code",[this._v("fly")]),this._v(" 时,编译器默认调用直接是现在类型上的方法,如示例 19-25 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Pilot "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Wizard "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Human"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Pilot "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Human "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"This is your captain speaking."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Wizard "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Human "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Up!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Human "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"*waving arms furiously*"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" person "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Human"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n person"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-25: 调用 "),s("code",[this._v("Human")]),this._v(" 实例的 "),s("code",[this._v("fly")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("运行这段代码会打印出 "),s("code",[this._v("*waving arms furiously*")]),this._v(",这表明 Rust 调用了直接实现在 "),s("code",[this._v("Human")]),this._v(" 上的 "),s("code",[this._v("fly")]),this._v(" 方法。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("为了能够调用 "),a("code",[t._v("Pilot")]),t._v(" trait 或 "),a("code",[t._v("Wizard")]),t._v(" trait 的 "),a("code",[t._v("fly")]),t._v(" 方法,我们需要使用更明显的语法以便能指定我们指的是哪个 "),a("code",[t._v("fly")]),t._v(" 方法。这个语法展示在示例 19-26 中:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Pilot "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Wizard "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Human"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Pilot "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Human "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"This is your captain speaking."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Wizard "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Human "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Up!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Human "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"*waving arms furiously*"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" person "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Human"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n Pilot"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("person"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n Wizard"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("person"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n person"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("fly")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-26: 指定我们希望调用哪一个 trait 的 "),s("code",[this._v("fly")]),this._v(" 方法")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在方法名前指定 trait 名向 Rust 澄清了我们希望调用哪个 "),s("code",[this._v("fly")]),this._v(" 实现。也可以选择写成 "),s("code",[this._v("Human::fly(&person)")]),this._v(",这等同于示例 19-26 中的 "),s("code",[this._v("person.fly()")]),this._v(",不过如果无需消歧义的话这么写就有点长了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("This is your captain speaking.\nUp!\n*waving arms furiously*\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("因为 "),a("code",[t._v("fly")]),t._v(" 方法获取一个 "),a("code",[t._v("self")]),t._v(" 参数,如果有两个 "),a("strong",[t._v("类型")]),t._v(" 都实现了同一 "),a("strong",[t._v("trait")]),t._v(",Rust 可以根据 "),a("code",[t._v("self")]),t._v(" 的类型计算出应该使用哪一个 trait 实现。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("然而,关联函数是 trait 的一部分,但没有 "),a("code",[t._v("self")]),t._v(" 参数。当同一作用域的两个类型实现了同一 trait,Rust 就不能计算出我们期望的是哪一个类型,除非使用 "),a("strong",[t._v("完全限定语法")]),t._v("("),a("em",[t._v("fully qualified syntax")]),t._v(")。例如,拿示例 19-27 中的 "),a("code",[t._v("Animal")]),t._v(" trait 来说,它有关联函数 "),a("code",[t._v("baby_name")]),t._v(",结构体 "),a("code",[t._v("Dog")]),t._v(" 实现了 "),a("code",[t._v("Animal")]),t._v(",同时有关联函数 "),a("code",[t._v("baby_name")]),t._v(" 直接定义于 "),a("code",[t._v("Dog")]),t._v(" 之上:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Animal "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("baby_name")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Dog"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Dog "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("baby_name")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Spot"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Animal "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Dog "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("baby_name")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"puppy"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"A baby dog is called a {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Dog"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("baby_name")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-27: 一个带有关联函数的 trait 和一个带有同名关联函数并实现了此 trait 的类型")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这段代码用于一个动物收容所,他们将所有的小狗起名为 Spot,这实现为定义于 "),a("code",[t._v("Dog")]),t._v(" 之上的关联函数 "),a("code",[t._v("baby_name")]),t._v("。"),a("code",[t._v("Dog")]),t._v(" 类型还实现了 "),a("code",[t._v("Animal")]),t._v(" trait,它描述了所有动物的共有的特征。小狗被称为 puppy,这表现为 "),a("code",[t._v("Dog")]),t._v(" 的 "),a("code",[t._v("Animal")]),t._v(" trait 实现中与 "),a("code",[t._v("Animal")]),t._v(" trait 相关联的函数 "),a("code",[t._v("baby_name")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 "),s("code",[this._v("main")]),this._v(" 调用了 "),s("code",[this._v("Dog::baby_name")]),this._v(" 函数,它直接调用了定义于 "),s("code",[this._v("Dog")]),this._v(" 之上的关联函数。这段代码会打印出:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("A baby dog is called a Spot\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这并不是我们需要的。我们希望调用的是 "),a("code",[t._v("Dog")]),t._v(" 上 "),a("code",[t._v("Animal")]),t._v(" trait 实现那部分的 "),a("code",[t._v("baby_name")]),t._v(" 函数,这样能够打印出 "),a("code",[t._v("A baby dog is called a puppy")]),t._v("。示例 19-26 中用到的技术在这并不管用;如果将 "),a("code",[t._v("main")]),t._v(" 改为示例 19-28 中的代码,则会得到一个编译错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n println!("A baby dog is called a {}", Animal::baby_name());\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-28: 尝试调用 "),s("code",[this._v("Animal")]),this._v(" trait 的 "),s("code",[this._v("baby_name")]),this._v(" 函数,不过 Rust 并不知道该使用哪一个实现")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为 "),s("code",[this._v("Animal::baby_name")]),this._v(" 是关联函数而不是方法,因此它没有 "),s("code",[this._v("self")]),this._v(" 参数,Rust 无法计算出所需的是哪一个 "),s("code",[this._v("Animal::baby_name")]),this._v(" 实现。我们会得到这个编译错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0283]: type annotations required: cannot resolve `_: Animal`\n --\x3e src/main.rs:20:43\n |\n20 | println!("A baby dog is called a {}", Animal::baby_name());\n | ^^^^^^^^^^^^^^^^^\n |\n = note: required by `Animal::baby_name`\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为了消歧义并告诉 Rust 我们希望使用的是 "),s("code",[this._v("Dog")]),this._v(" 的 "),s("code",[this._v("Animal")]),this._v(" 实现,需要使用 "),s("strong",[this._v("完全限定语法")]),this._v(",这是调用函数时最为明确的方式。示例 19-29 展示了如何使用完全限定语法:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Animal "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("baby_name")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Dog"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Dog "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("baby_name")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Spot"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Animal "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Dog "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("baby_name")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"puppy"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"A baby dog is called a {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Dog "),a("span",{attrs:{class:"token keyword"}},[t._v("as")]),t._v(" Animal"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("baby_name")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-29: 使用完全限定语法来指定我们希望调用的是 "),s("code",[this._v("Dog")]),this._v(" 上 "),s("code",[this._v("Animal")]),this._v(" trait 实现中的 "),s("code",[this._v("baby_name")]),this._v(" 函数")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("我们在尖括号中向 Rust 提供了类型注解,并通过在此函数调用中将 "),a("code",[t._v("Dog")]),t._v(" 类型当作 "),a("code",[t._v("Animal")]),t._v(" 对待,来指定希望调用的是 "),a("code",[t._v("Dog")]),t._v(" 上 "),a("code",[t._v("Animal")]),t._v(" trait 实现中的 "),a("code",[t._v("baby_name")]),t._v(" 函数。现在这段代码会打印出我们期望的数据:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("A baby dog is called a puppy\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("::function(receiver_if_method, next_arg, ...);\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("对于关联函数,其没有一个 "),s("code",[this._v("receiver")]),this._v(",故只会有其他参数的列表。可以选择在任何函数或方法调用处使用完全限定语法。然而,允许省略任何 Rust 能够从程序中的其他信息中计算出的部分。只有当存在多个同名实现而 Rust 需要帮助以便知道我们希望调用哪个实现时,才需要使用这个较为冗长的语法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"父-trait-用于在另一个-trait-中使用某-trait-的功能"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#父-trait-用于在另一个-trait-中使用某-trait-的功能","aria-hidden":"true"}},[this._v("#")]),this._v(" 父 trait 用于在另一个 trait 中使用某 trait 的功能")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("有时我们可能会需要某个 trait 使用另一个 trait 的功能。在这种情况下,需要能够依赖相关的 trait 也被实现。这个所需的 trait 是我们实现的 trait 的 "),s("strong",[this._v("父(超) trait")]),this._v("("),s("em",[this._v("supertrait")]),this._v(")。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("例如我们希望创建一个带有 "),a("code",[t._v("outline_print")]),t._v(" 方法的 trait "),a("code",[t._v("OutlinePrint")]),t._v(",它会打印出带有星号框的值。也就是说,如果 "),a("code",[t._v("Point")]),t._v(" 实现了 "),a("code",[t._v("Display")]),t._v(" 并返回 "),a("code",[t._v("(x, y)")]),t._v(",调用以 1 作为 "),a("code",[t._v("x")]),t._v(" 和 3 作为 "),a("code",[t._v("y")]),t._v(" 的 "),a("code",[t._v("Point")]),t._v(" 实例的 "),a("code",[t._v("outline_print")]),t._v(" 会显示如下:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("**********\n* *\n* (1, 3) *\n* *\n**********\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("在 "),a("code",[t._v("outline_print")]),t._v(" 的实现中,因为希望能够使用 "),a("code",[t._v("Display")]),t._v(" trait 的功能,则需要说明 "),a("code",[t._v("OutlinePrint")]),t._v(" 只能用于同时也实现了 "),a("code",[t._v("Display")]),t._v(" 并提供了 "),a("code",[t._v("OutlinePrint")]),t._v(" 需要的功能的类型。可以通过在 trait 定义中指定 "),a("code",[t._v("OutlinePrint: Display")]),t._v(" 来做到这一点。这类似于为 trait 增加 trait bound。示例 19-30 展示了一个 "),a("code",[t._v("OutlinePrint")]),t._v(" trait 的实现:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" OutlinePrint"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Display "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("outline_print")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" output "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("to_string")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" len "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" output"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("len")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"*"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("repeat")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("len "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"*{}*"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('" "')]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("repeat")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("len "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"* {} *"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" output"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"*{}*"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('" "')]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("repeat")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("len "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"*"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("repeat")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("len "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-30: 实现 "),s("code",[this._v("OutlinePrint")]),this._v(" trait,它要求来自 "),s("code",[this._v("Display")]),this._v(" 的功能")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("因为指定了 "),a("code",[t._v("OutlinePrint")]),t._v(" 需要 "),a("code",[t._v("Display")]),t._v(" trait,则可以在 "),a("code",[t._v("outline_print")]),t._v(" 中使用 "),a("code",[t._v("to_string")]),t._v(", 其会为任何实现 "),a("code",[t._v("Display")]),t._v(" 的类型自动实现。如果不在 trait 名后增加 "),a("code",[t._v(": Display")]),t._v(" 并尝试在 "),a("code",[t._v("outline_print")]),t._v(" 中使用 "),a("code",[t._v("to_string")]),t._v(",则会得到一个错误说在当前作用域中没有找到用于 "),a("code",[t._v("&Self")]),t._v(" 类型的方法 "),a("code",[t._v("to_string")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们看看如果尝试在一个没有实现 "),s("code",[this._v("Display")]),this._v(" 的类型上实现 "),s("code",[this._v("OutlinePrint")]),this._v(" 会发生什么,比如 "),s("code",[this._v("Point")]),this._v(" 结构体:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" OutlinePrint "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" OutlinePrint "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这样会得到一个错误说 "),s("code",[this._v("Display")]),this._v(" 是必须的而未被实现:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0277]: the trait bound `Point: std::fmt::Display` is not satisfied\n --\x3e src/main.rs:20:6\n |\n20 | impl OutlinePrint for Point {}\n | ^^^^^^^^^^^^ `Point` cannot be formatted with the default formatter;\n try using `:?` instead if you are using a format string\n |\n = help: the trait `std::fmt::Display` is not implemented for `Point`\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("一旦在 "),s("code",[this._v("Point")]),this._v(" 上实现 "),s("code",[this._v("Display")]),this._v(" 并满足 "),s("code",[this._v("OutlinePrint")]),this._v(" 要求的限制,比如这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Display "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fmt")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Formatter"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Result "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("write!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"({}, {})"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("y"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("那么在 "),a("code",[t._v("Point")]),t._v(" 上实现 "),a("code",[t._v("OutlinePrint")]),t._v(" trait 将能成功编译并可以在 "),a("code",[t._v("Point")]),t._v(" 实例上调用 "),a("code",[t._v("outline_print")]),t._v(" 来显示位于星号框中的点的值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"newtype-模式用以在外部类型上实现外部-trait"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#newtype-模式用以在外部类型上实现外部-trait","aria-hidden":"true"}},[this._v("#")]),this._v(" newtype 模式用以在外部类型上实现外部 trait")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在第十章的 “为类型实现 trait” 部分,我们提到了孤儿规则(orphan rule),它说明只要 trait 或类型对于当前 crate 是本地的话就可以在此类型上实现该 trait。一个绕开这个限制的方法是使用"),s("strong",[this._v("newtype 模式")]),this._v("("),s("em",[this._v("newtype pattern")]),this._v("),它涉及到在一个元组结构体(第五章 “用没有命名字段的元组结构体来创建不同的类型” 部分介绍了元组结构体)中创建一个新类型。这个元组结构体带有一个字段作为希望实现 trait 的类型的简单封装。接着这个封装类型对于 crate 是本地的,这样就可以在这个封装上实现 trait。“Newtype” 是一个源自(U.C.0079,逃)Haskell 编程语言的概念。使用这个模式没有运行时性能惩罚,这个封装类型在编译时就被省略了。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("例如,如果想要在 "),a("code",[t._v("Vec")]),t._v(" 上实现 "),a("code",[t._v("Display")]),t._v(",而孤儿规则阻止我们直接这么做,因为 "),a("code",[t._v("Display")]),t._v(" trait 和 "),a("code",[t._v("Vec")]),t._v(" 都定义于我们的 crate 之外。可以创建一个包含 "),a("code",[t._v("Vec")]),t._v(" 实例的 "),a("code",[t._v("Wrapper")]),t._v(" 结构体,接着可以如列表 19-31 那样在 "),a("code",[t._v("Wrapper")]),t._v(" 上实现 "),a("code",[t._v("Display")]),t._v(" 并使用 "),a("code",[t._v("Vec")]),t._v(" 的值:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("可以创建一个包含 "),a("code",[t._v("Vec")]),t._v(" 实例的 "),a("code",[t._v("Wrapper")]),t._v(" 结构体。接着可以如列表 19-30 那样在 "),a("code",[t._v("Wrapper")]),t._v(" 上实现 "),a("code",[t._v("Display")]),t._v(" 并使用 "),a("code",[t._v("Vec")]),t._v(" 的值:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("Wrapper")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Vec"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("String"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Display "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Wrapper "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("fmt")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Formatter"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Result "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("write!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"[{}]"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("join")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('", "')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" w "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("Wrapper")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"world"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"w = {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" w"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-31: 创建 "),s("code",[this._v("Wrapper")]),this._v(" 类型封装 "),s("code",[this._v("Vec")]),this._v(" 以便能够实现 "),s("code",[this._v("Display")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("Display")]),t._v(" 的实现使用 "),a("code",[t._v("self.0")]),t._v(" 来访问其内部的 "),a("code",[t._v("Vec")]),t._v(",因为 "),a("code",[t._v("Wrapper")]),t._v(" 是元组结构体而 "),a("code",[t._v("Vec")]),t._v(" 是结构体总位于索引 0 的项。接着就可以使用 "),a("code",[t._v("Wrapper")]),t._v(" 中 "),a("code",[t._v("Display")]),t._v(" 的功能了。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("此方法的缺点是,因为 "),a("code",[t._v("Wrapper")]),t._v(" 是一个新类型,它没有定义于其值之上的方法;必须直接在 "),a("code",[t._v("Wrapper")]),t._v(" 上实现 "),a("code",[t._v("Vec")]),t._v(" 的所有方法,这样就可以代理到"),a("code",[t._v("self.0")]),t._v(" 上 —— 这就允许我们完全像 "),a("code",[t._v("Vec")]),t._v(" 那样对待 "),a("code",[t._v("Wrapper")]),t._v("。如果希望新类型拥有其内部类型的每一个方法,为封装类型实现 "),a("code",[t._v("Deref")]),t._v(" trait(第十五章 “通过 "),a("code",[t._v("Deref")]),t._v(" trait 将智能指针当作常规引用处理” 部分讨论过)并返回其内部类型是一种解决方案。如果不希望封装类型拥有所有内部类型的方法,比如为了限制封装类型的行为,则必须只自行实现所需的方法。")])}],!1,null,null,null);e.options.__file="ch19-03-advanced-traits.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/101.80606dd4.js b/src/.vuepress/dist/assets/js/101.80606dd4.js
new file mode 100644
index 0000000..1a7d42d
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/101.80606dd4.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[101],{169:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch19-04-advanced-types.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch19-04-advanced-types.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit 9d5b9a573daf5fa0c98b3a3005badcea4a0a5211")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),a("p",[t._v("另一个 newtype 模式的应用在于抽象掉一些类型的实现细节:例如,封装类型可以暴露出与直接使用其内部私有类型时所不同的 API,以便限制其功能。")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._m(11),t._v(" "),a("p",[t._v("类型别名的主要用途是减少重复。例如,可能会有这样很长的类型:")]),t._v(" "),t._m(12),a("p",[t._v("在函数签名或类型注解中每次都书写这个类型将是枯燥且易于出错的。想象一下如示例 19-32 这样全是如此代码的项目:")]),t._v(" "),t._m(13),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._m(29),t._v(" "),a("p",[t._v("不过一个不能创建值的类型有什么用呢?如果你回想一下第二章,曾经有一些看起来像这样的代码,如示例 19-34 所重现的:")]),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._v(" "),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),a("p",[t._v("实际上被当作如下处理:")]),t._v(" "),t._m(54),a("p",[t._v("泛型函数默认只能用于在编译时已知大小的类型。然而可以使用如下特殊语法来放宽这个限制:")]),t._v(" "),t._m(55),t._m(56),t._v(" "),t._m(57),t._v(" "),a("p",[t._v("接下来,让我们讨论一下函数和闭包!")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"高级类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#高级类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 高级类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 的类型系统有一些我们曾经提到但没有讨论过的功能。首先我们从一个关于为什么 newtype 与类型一样有用的更宽泛的讨论开始。接着会转向类型别名(type aliases),一个类似于 newtype 但有着稍微不同的语义的功能。我们还会讨论 "),s("code",[this._v("!")]),this._v(" 类型和动态大小类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"为了类型安全和抽象而使用-newtype-模式"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#为了类型安全和抽象而使用-newtype-模式","aria-hidden":"true"}},[this._v("#")]),this._v(" 为了类型安全和抽象而使用 newtype 模式")])},function(){var t=this.$createElement,s=this._self._c||t;return s("blockquote",[s("p",[this._v("这一部分假设你已经阅读了 “高级 trait” 部分的 newtype 模式相关内容。")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("newtype 模式可以用于一些其他我们还未讨论的功能,包括静态的确保某值不被混淆,和用来表示一个值的单元。实际上示例 19-23 中已经有一个这样的例子:"),a("code",[t._v("Millimeters")]),t._v(" 和 "),a("code",[t._v("Meters")]),t._v(" 结构体都在 newtype 中封装了 "),a("code",[t._v("u32")]),t._v(" 值。如果编写了一个有 "),a("code",[t._v("Millimeters")]),t._v(" 类型参数的函数,不小心使用 "),a("code",[t._v("Meters")]),t._v(" 或普通的 "),a("code",[t._v("u32")]),t._v(" 值来调用该函数的程序是不能编译的。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("newtype 也可以隐藏其内部的泛型类型。例如,可以提供一个封装了 "),a("code",[t._v("HashMap")]),t._v(" 的 "),a("code",[t._v("People")]),t._v(" 类型,用来储存人名以及相应的 ID。使用 "),a("code",[t._v("People")]),t._v(" 的代码只需与提供的公有 API 交互即可,比如向 "),a("code",[t._v("People")]),t._v(" 集合增加名字字符串的方法,这样这些代码就无需知道在内部我们将一个 "),a("code",[t._v("i32")]),t._v(" ID 赋予了这个名字了。newtype 模式是一种实现第十七章 “封装隐藏了实现细节” 部分所讨论的隐藏实现细节的封装的轻量级方法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"类型别名用来创建类型同义词"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#类型别名用来创建类型同义词","aria-hidden":"true"}},[this._v("#")]),this._v(" 类型别名用来创建类型同义词")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("连同 newtype 模式,Rust 还提供了声明 "),a("strong",[t._v("类型别名")]),t._v("("),a("em",[t._v("type alias")]),t._v(")的能力,使用 "),a("code",[t._v("type")]),t._v(" 关键字来给予现有类型另一个名字。例如,可以像这样创建 "),a("code",[t._v("i32")]),t._v(" 的别名 "),a("code",[t._v("Kilometers")]),t._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[this._v("type")]),this._v(" Kilometers "),s("span",{attrs:{class:"token operator"}},[this._v("=")]),this._v(" i32"),s("span",{attrs:{class:"token punctuation"}},[this._v(";")]),this._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这意味着 "),a("code",[t._v("Kilometers")]),t._v(" 是 "),a("code",[t._v("i32")]),t._v(" 的 "),a("strong",[t._v("同义词")]),t._v("("),a("em",[t._v("synonym")]),t._v(");不同于示例 19-23 中创建的 "),a("code",[t._v("Millimeters")]),t._v(" 和 "),a("code",[t._v("Meters")]),t._v(" 类型。"),a("code",[t._v("Kilometers")]),t._v(" 不是一个新的、单独的类型。"),a("code",[t._v("Kilometers")]),t._v(" 类型的值将被完全当作 "),a("code",[t._v("i32")]),t._v(" 类型值来对待:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Kilometers "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Kilometers "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"x + y = {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("因为 "),a("code",[t._v("Kilometers")]),t._v(" 是 "),a("code",[t._v("i32")]),t._v(" 的别名,他们是同一类型,可以将 "),a("code",[t._v("i32")]),t._v(" 与 "),a("code",[t._v("Kilometers")]),t._v(" 相加,也可以将 "),a("code",[t._v("Kilometers")]),t._v(" 传递给获取 "),a("code",[t._v("i32")]),t._v(" 参数的函数。但通过这种手段无法获得上一部分讨论的 newtype 模式所提供的类型检查的好处。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("Box\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" f"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Box"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),a("span",{attrs:{class:"token function"}},[t._v("Fn")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Box"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("||")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"hi"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("takes_long_type")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Box"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),a("span",{attrs:{class:"token function"}},[t._v("Fn")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("returns_long_type")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Box"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),a("span",{attrs:{class:"token function"}},[t._v("Fn")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n# Box"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("||")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-32: 在很多地方使用名称很长的类型")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("类型别名通过减少项目中重复代码的数量来使其更加易于控制。这里我们为这个冗长的类型引入了一个叫做 "),s("code",[this._v("Thunk")]),this._v(" 的别名,这样就可以如示例 19-33 所示将所有使用这个类型的地方替换为更短的 "),s("code",[this._v("Thunk")]),this._v(":")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Thunk "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Box"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),a("span",{attrs:{class:"token function"}},[t._v("Fn")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" f"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Thunk "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Box"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("||")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"hi"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("takes_long_type")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Thunk"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("returns_long_type")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Thunk "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n# Box"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("||")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-33: 引入类型别名 "),s("code",[this._v("Thunk")]),this._v(" 来减少重复")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这样就读写起来就容易多了!为类型别名选择一个好名字也可以帮助你表达意图(单词 "),s("em",[this._v("thunk")]),this._v(" 表示会在之后被计算的代码,所以这是一个存放闭包的合适的名字)。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("类型别名也经常与 "),a("code",[t._v("Result")]),t._v(" 结合使用来减少重复。考虑一下标准库中的 "),a("code",[t._v("std::io")]),t._v(" 模块。I/O 操作通常会返回一个 "),a("code",[t._v("Result")]),t._v(",因为这些操作可能会失败。标准库中的 "),a("code",[t._v("std::io::Error")]),t._v(" 结构体代表了所有可能的 I/O 错误。"),a("code",[t._v("std::io")]),t._v(" 中大部分函数会返回 "),a("code",[t._v("Result")]),t._v(",其中 "),a("code",[t._v("E")]),t._v(" 是 "),a("code",[t._v("std::io::Error")]),t._v(",比如 "),a("code",[t._v("Write")]),t._v(" trait 中的这些函数:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Error"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Write "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("write")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" buf"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("u8"),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("usize"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Error"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("flush")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Error"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("write_all")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" buf"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("u8"),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Error"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("write_fmt")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Arguments"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Error"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里出现了很多的 "),s("code",[this._v("Result<..., Error>")]),this._v("。为此,"),s("code",[this._v("std::io")]),this._v(" 有这个类型别名声明:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("type Result = Result;\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("因为这位于 "),a("code",[t._v("std::io")]),t._v(" 中,可用的完全限定的别名是 "),a("code",[t._v("std::io::Result")]),t._v(";也就是说,"),a("code",[t._v("Result")]),t._v(" 中 "),a("code",[t._v("E")]),t._v(" 放入了 "),a("code",[t._v("std::io::Error")]),t._v("。"),a("code",[t._v("Write")]),t._v(" trait 中的函数最终看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("pub trait Write {\n fn write(&mut self, buf: &[u8]) -> Result;\n fn flush(&mut self) -> Result<()>;\n\n fn write_all(&mut self, buf: &[u8]) -> Result<()>;\n fn write_fmt(&mut self, fmt: Arguments) -> Result<()>;\n}\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("类型别名在两个方面有帮助:易于编写 "),a("strong",[t._v("并")]),t._v(" 在整个 "),a("code",[t._v("std::io")]),t._v(" 中提供了一致的接口。因为这是一个别名,它只是另一个 "),a("code",[t._v("Result")]),t._v(",这意味着可以在其上使用 "),a("code",[t._v("Result")]),t._v(" 的任何方法,以及像 "),a("code",[t._v("?")]),t._v(" 这样的特殊语法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"从不返回的-,never-type"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#从不返回的-,never-type","aria-hidden":"true"}},[this._v("#")]),this._v(" 从不返回的 "),s("code",[this._v("!")]),this._v(",never type")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 有一个叫做 "),s("code",[this._v("!")]),this._v(" 的特殊类型。在类型理论术语中,它被称为 "),s("em",[this._v("empty type")]),this._v(",因为它没有值。我们更倾向于称之为 "),s("em",[this._v("never type")]),this._v("。这个名字描述了它的作用:在函数从不返回的时候充当返回值。例如:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn bar() -> ! {\n // --snip--\n}\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这读 “函数 "),a("code",[t._v("bar")]),t._v(" 从不返回”,而从不返回的函数被称为 "),a("strong",[t._v("发散函数")]),t._v("("),a("em",[t._v("diverging functions")]),t._v(")。不能创建 "),a("code",[t._v("!")]),t._v(" 类型的值,所以 "),a("code",[t._v("bar")]),t._v(" 也不可能返回。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" guess "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"3"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("loop")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" guess"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" guess"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("trim")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("parse")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("Ok")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("num"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" num"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("Err")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("continue")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("break")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-34: "),s("code",[this._v("match")]),this._v(" 语句和一个以 "),s("code",[this._v("continue")]),this._v(" 结束的分支")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当时我们忽略了代码中的一些细节。在第六章 “"),s("code",[this._v("match")]),this._v(" 控制流运算符” 部分,我们学习了 "),s("code",[this._v("match")]),this._v(" 的分支必须返回相同的类型。如下代码不能工作:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let guess = match guess.trim().parse() {\n Ok(_) => 5,\n Err(_) => "hello",\n}\n')])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这里的 "),a("code",[t._v("guess")]),t._v(" 必须既是整型也是字符串,而 Rust 要求 "),a("code",[t._v("guess")]),t._v(" 只能是一个类型。那么 "),a("code",[t._v("continue")]),t._v(" 返回了什么呢?为什么示例 19-34 中会允许一个分支返回 "),a("code",[t._v("u32")]),t._v(" 而另一个分支却以 "),a("code",[t._v("continue")]),t._v(" 结束呢?")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("正如你可能猜到的,"),a("code",[t._v("continue")]),t._v(" 的值是 "),a("code",[t._v("!")]),t._v("。也就是说,当 Rust 要计算 "),a("code",[t._v("guess")]),t._v(" 的类型时,它查看这两个分支。前者是 "),a("code",[t._v("u32")]),t._v(" 值,而后者是 "),a("code",[t._v("!")]),t._v(" 值。因为 "),a("code",[t._v("!")]),t._v(" 并没有一个值,Rust 决定 "),a("code",[t._v("guess")]),t._v(" 的类型是 "),a("code",[t._v("u32")]),t._v("。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("描述 "),a("code",[t._v("!")]),t._v(" 的行为的正式方式是 never type 可以强转为任何其他类型。允许 "),a("code",[t._v("match")]),t._v(" 的分支以 "),a("code",[t._v("continue")]),t._v(" 结束是因为 "),a("code",[t._v("continue")]),t._v(" 并不真正返回一个值;相反它把控制权交回上层循环,所以在 "),a("code",[t._v("Err")]),t._v(" 的情况,事实上并未对 "),a("code",[t._v("guess")]),t._v(" 赋值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("never type 的另一个用途是 "),s("code",[this._v("panic!")]),this._v("。还记得 "),s("code",[this._v("Option")]),this._v(" 上的 "),s("code",[this._v("unwrap")]),this._v(" 函数吗?它产生一个值或 panic。这里是它的定义:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('impl Option {\n pub fn unwrap(self) -> T {\n match self {\n Some(val) => val,\n None => panic!("called `Option::unwrap()` on a `None` value"),\n }\n }\n}\n')])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这里与示例 19-34 中的 "),a("code",[t._v("match")]),t._v(" 发生了相同的情况:我们知道 "),a("code",[t._v("val")]),t._v(" 是 "),a("code",[t._v("T")]),t._v(" 类型,"),a("code",[t._v("panic!")]),t._v(" 是 "),a("code",[t._v("!")]),t._v(" 类型,所以整个 "),a("code",[t._v("match")]),t._v(" 表达式的结果是 "),a("code",[t._v("T")]),t._v(" 类型。这能工作是因为 "),a("code",[t._v("panic!")]),t._v(" 并不产生一个值;它会终止程序。对于 "),a("code",[t._v("None")]),t._v(" 的情况,"),a("code",[t._v("unwrap")]),t._v(" 并不返回一个值,所以这些代码是有效。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("最后一个有着 "),s("code",[this._v("!")]),this._v(" 类型的表达式是 "),s("code",[this._v("loop")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('print!("forever ");\n\nloop {\n print!("and ever ");\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里,循环永远也不结束,所以此表达式的值是 "),s("code",[this._v("!")]),this._v("。但是如果引入 "),s("code",[this._v("break")]),this._v(" 这就不为真了,因为循环在执行到 "),s("code",[this._v("break")]),this._v(" 后就会终止。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"动态大小类型和-sized-trait"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#动态大小类型和-sized-trait","aria-hidden":"true"}},[this._v("#")]),this._v(" 动态大小类型和 "),s("code",[this._v("Sized")]),this._v(" trait")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为 Rust 需要知道例如应该为特定类型的值分配多少空间这样的信息其类型系统的一个特定的角落可能令人迷惑:这就是 "),s("strong",[this._v("动态大小类型")]),this._v("("),s("em",[this._v("dynamically sized types")]),this._v(")的概念。这有时被称为 “DST” 或 “unsized types”,这些类型允许我们处理只有在运行时才知道大小的类型。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("让我们深入研究一个贯穿本书都在使用的动态大小类型的细节:"),a("code",[t._v("str")]),t._v("。没错,不是 "),a("code",[t._v("&str")]),t._v(",而是 "),a("code",[t._v("str")]),t._v(" 本身。"),a("code",[t._v("str")]),t._v(" 是一个 DST;直到运行时我们都不知道字符串有多长。因为直到运行时都不能知道大其小,也就意味着不能创建 "),a("code",[t._v("str")]),t._v(" 类型的变量,也不能获取 "),a("code",[t._v("str")]),t._v(" 类型的参数。考虑一下这些代码,他们不能工作:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let s1: str = "Hello there!";\nlet s2: str = "How\'s it going?";\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 需要知道应该为特定类型的值分配多少内存,同时所有同一类型的值必须使用相同数量的内存。如果允许编写这样的代码,也就意味着这两个 "),s("code",[this._v("str")]),this._v(" 需要占用完全相同大小的空间,不过它们有着不同的长度。这也就是为什么不可能创建一个存放动态大小类型的变量的原因。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("那么该怎么办呢?你已经知道了这种问题的答案:"),a("code",[t._v("s1")]),t._v(" 和 "),a("code",[t._v("s2")]),t._v(" 的类型是 "),a("code",[t._v("&str")]),t._v(" 而不是 "),a("code",[t._v("str")]),t._v("。如果你回想第四章 “字符串 slice” 部分,slice 数据结储存了开始位置和 slice 的长度。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("所以虽然 "),a("code",[t._v("&T")]),t._v(" 是一个储存了 "),a("code",[t._v("T")]),t._v(" 所在的内存位置的单个值,"),a("code",[t._v("&str")]),t._v(" 则是 "),a("strong",[t._v("两个")]),t._v(" 值:"),a("code",[t._v("str")]),t._v(" 的地址和其长度。这样,"),a("code",[t._v("&str")]),t._v(" 就有了一个在编译时可以知道的大小:它是 "),a("code",[t._v("usize")]),t._v(" 长度的两倍。也就是说,我们总是知道 "),a("code",[t._v("&str")]),t._v(" 的大小,而无论其引用的字符串是多长。这里是 Rust 中动态大小类型的常规用法:他们有一些额外的元信息来储存动态信息的大小。这引出了动态大小类型的黄金规则:必须将动态大小类型的值置于某种指针之后。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("可以将 "),a("code",[t._v("str")]),t._v(" 与所有类型的指针结合:比如 "),a("code",[t._v("Box")]),t._v(" 或 "),a("code",[t._v("Rc")]),t._v("。事实上,之前我们已经见过了,不过是另一个动态大小类型:trait。每一个 trait 都是一个可以通过 trait 名称来引用的动态大小类型。在第十七章 “为使用不同类型的值而设计的 trait 对象” 部分,我们提到了为了将 trait 用于 trait 对象,必须将他们放入指针之后,比如 "),a("code",[t._v("&Trait")]),t._v(" 或 "),a("code",[t._v("Box")]),t._v("("),a("code",[t._v("Rc")]),t._v(" 也可以)。trait 之所以是动态大小类型的是因为只有这样才能使用它。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"sized-trait"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#sized-trait","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("Sized")]),this._v(" trait")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为了处理 DST,Rust 有一个特定的 trait 来决定一个类型的大小是否在编译时可知:这就是 "),s("code",[this._v("Sized")]),this._v(" trait。这个 trait 自动为编译器在编译时就知道大小的类型实现。另外,Rust 隐式的为每一个泛型函数增加了 "),s("code",[this._v("Sized")]),this._v(" bound。也就是说,对于如下泛型函数定义:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn generic(t: T) {\n // --snip--\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn generic(t: T) {\n // --snip--\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn generic(t: &T) {\n // --snip--\n}\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("?Sized")]),t._v(" trait bound 与 "),a("code",[t._v("Sized")]),t._v(" 相对;也就是说,它可以读作 “"),a("code",[t._v("T")]),t._v(" 可能是也可能不是 "),a("code",[t._v("Sized")]),t._v(" 的”。这个语法只能用于 "),a("code",[t._v("Sized")]),t._v(" ,而不能用于其他 trait。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("另外注意我们将 "),a("code",[t._v("t")]),t._v(" 参数的类型从 "),a("code",[t._v("T")]),t._v(" 变为了 "),a("code",[t._v("&T")]),t._v(":因为其类型可能不是 "),a("code",[t._v("Sized")]),t._v(" 的,所以需要将其置于某种指针之后。在这个例子中选择了引用。")])}],!1,null,null,null);e.options.__file="ch19-04-advanced-types.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/102.75bf264b.js b/src/.vuepress/dist/assets/js/102.75bf264b.js
new file mode 100644
index 0000000..b6d5abd
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/102.75bf264b.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[102],{168:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch19-05-advanced-functions-and-closures.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch19-05-advanced-functions-and-closures.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 509cb42ece610bdac8eaad26d57fb604dc078623")])]),t._v(" "),n("p",[t._v("最后我们将探索一些有关函数和闭包的高级功能:函数指针以及返回值闭包。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._m(14),t._v(" "),n("p",[t._v("一些人倾向于函数风格,一些人喜欢闭包。这两种形式最终都会产生同样的代码,所以请使用对你来说更明白的形式吧。")]),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),n("p",[t._v("这段代码尝试直接返回闭包,它并不能编译:")]),t._v(" "),t._m(17),n("p",[t._v("编译器给出的错误是:")]),t._v(" "),t._m(18),t._m(19),t._v(" "),t._m(20),n("p",[t._v("这段代码正好可以编译。关于 trait 对象的更多内容,请回顾第十七章的 “为使用不同类型的值而设计的 trait 对象” 部分。")]),t._v(" "),t._m(21),t._v(" "),n("p",[t._v("好的!现在我们学习了 Rust 并不常用但在特定情况下你可能用得着的功能。我们介绍了很多复杂的主题,这样若你在错误信息提示或阅读他人代码时遇到他们,至少可以说之前已经见过这些概念和语法了。你可以使用本章作为一个解决方案的参考。")]),t._v(" "),n("p",[t._v("接下来,我们将再开始一个项目,将本书所学的所有内容付与实践!")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"高级函数与闭包"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#高级函数与闭包","aria-hidden":"true"}},[this._v("#")]),this._v(" 高级函数与闭包")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"函数指针"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#函数指针","aria-hidden":"true"}},[this._v("#")]),this._v(" 函数指针")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们讨论过了如何向函数传递闭包;也可以向函数传递常规函数!这在我们希望传递已经定义的函数而不是重新定义闭包作为参数是很有用。通过函数指针允许我们使用函数作为另一个函数的参数。函数的类型是 "),n("code",[t._v("fn")]),t._v(" (使用小写的 “f” )以免与 "),n("code",[t._v("Fn")]),t._v(" 闭包 trait 相混淆。"),n("code",[t._v("fn")]),t._v(" 被称为"),n("strong",[t._v("函数指针")]),t._v("("),n("em",[t._v("function pointer")]),t._v(")。指定参数为函数指针的语法类似于闭包,如示例 19-34 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("add_one")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("do_twice")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("f")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("f")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" answer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("do_twice")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("add_one"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The answer is: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" answer"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 19-35: 使用 "),s("code",[this._v("fn")]),this._v(" 类型接受函数指针作为参数")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这会打印出 "),n("code",[t._v("The answer is: 12")]),t._v("。"),n("code",[t._v("do_twice")]),t._v(" 中的 "),n("code",[t._v("f")]),t._v(" 被指定为一个接受一个 "),n("code",[t._v("i32")]),t._v(" 参数并返回 "),n("code",[t._v("i32")]),t._v(" 的 "),n("code",[t._v("fn")]),t._v("。接着就可以在 "),n("code",[t._v("do_twice")]),t._v(" 函数体中调用 "),n("code",[t._v("f")]),t._v("。在 "),n("code",[t._v("main")]),t._v(" 中,可以将函数名 "),n("code",[t._v("add_one")]),t._v(" 作为第一个参数传递给 "),n("code",[t._v("do_twice")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不同于闭包,"),s("code",[this._v("fn")]),this._v(" 是一个类型而不是一个 trait,所以直接指定 "),s("code",[this._v("fn")]),this._v(" 作为参数而不是声明一个带有 "),s("code",[this._v("Fn")]),this._v(" 作为 trait bound 的泛型参数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("函数指针实现了所有三个闭包 trait("),s("code",[this._v("Fn")]),this._v("、"),s("code",[this._v("FnMut")]),this._v(" 和 "),s("code",[this._v("FnOnce")]),this._v("),所以总是可以在调用期望闭包的函数时传递函数指针作为参数。倾向于编写使用泛型和闭包 trait 的函数,这样它就能接受函数或闭包作为参数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("一个只期望接受 "),s("code",[this._v("fn")]),this._v(" 而不接受闭包的情况的例子是与不存在闭包的外部代码交互时:C 语言的函数可以接受函数作为参数,但没有闭包。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("作为一个既可以使用内联定义的闭包又可以使用命名函数的例子,让我们看看一个 "),s("code",[this._v("map")]),this._v(" 的应用。使用 "),s("code",[this._v("map")]),this._v(" 函数将一个数字 vector 转换为一个字符串 vector,就可以使用闭包,比如这样:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" list_of_numbers "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" list_of_strings"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("String"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" list_of_numbers\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("map")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v("i"),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v(" i"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("to_string")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("collect")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("或者可以将函数作为 "),s("code",[this._v("map")]),this._v(" 的参数来代替闭包,像是这样:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" list_of_numbers "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" list_of_strings"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("String"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" list_of_numbers\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("map")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ToString"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("to_string"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("collect")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意这里必须使用 “高级 trait” 部分讲到的完全限定语法,因为存在多个叫做 "),n("code",[t._v("to_string")]),t._v(" 的函数;这里使用了定义于 "),n("code",[t._v("ToString")]),t._v(" trait 的 "),n("code",[t._v("to_string")]),t._v(" 函数,标准库为所有实现了 "),n("code",[t._v("Display")]),t._v(" 的类型实现了这个 trait。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"返回闭包"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#返回闭包","aria-hidden":"true"}},[this._v("#")]),this._v(" 返回闭包")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("闭包表现为 trait,这意味着不能直接返回闭包。对于大部分需要返回 trait 的情况,可以使用实现了期望返回的 trait 的具体类型来替代函数的返回值。但是这不能用于闭包,因为他们没有一个可返回的具体类型;例如不允许使用函数指针 "),s("code",[this._v("fn")]),this._v(" 作为返回值类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn returns_closure() -> Fn(i32) -> i32 {\n |x| x + 1\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0277]: the trait bound `std::ops::Fn(i32) -> i32 + 'static:\nstd::marker::Sized` is not satisfied\n --\x3e\n |\n1 | fn returns_closure() -> Fn(i32) -> i32 {\n | ^^^^^^^^^^^^^^ `std::ops::Fn(i32) -> i32 + 'static`\n does not have a constant size known at compile-time\n |\n = help: the trait `std::marker::Sized` is not implemented for\n `std::ops::Fn(i32) -> i32 + 'static`\n = note: the return type of a function must have a statically known size\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("错误又一次指向了 "),s("code",[this._v("Sized")]),this._v(" trait!Rust 并不知道需要多少空间来储存闭包。不过我们在上一部分见过这种情况的解决办法:可以使用 trait 对象:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("returns_closure")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Box"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token function"}},[t._v("Fn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Box"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v("x"),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])}],!1,null,null,null);e.options.__file="ch19-05-advanced-functions-and-closures.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/103.7615fac4.js b/src/.vuepress/dist/assets/js/103.7615fac4.js
new file mode 100644
index 0000000..2f31fff
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/103.7615fac4.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[103],{246:function(e,t,r){"use strict";r.r(t);var s=r(0),n=Object(s.a)({},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"content"},[e._m(0),e._v(" "),r("blockquote",[r("p",[r("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch20-00-final-project-a-web-server.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ch20-00-final-project-a-web-server.md"),r("OutboundLink")],1),e._v(" "),r("br"),e._v("\ncommit e2a38b44f3a7f796fa8000e558dc8dd2ddf340a3")])]),e._v(" "),r("p",[e._v("这是一次漫长的旅途,不过我们做到了!这一章便是本书的结束。离别是如此甜蜜的悲伤。不过在我们结束之前,再来一起构建另一个项目,来展示最后几章所学,同时复习更早的章节。")]),e._v(" "),r("p",[e._v("作为最后的项目,我们将要实现一个只返回 “hello” 的 web server;它在浏览器中看起来就如图例 20-1 所示:")]),e._v(" "),e._m(1),e._v(" "),e._m(2),e._v(" "),r("p",[e._v("如下是我们将怎样构建此 web server 的计划:")]),e._v(" "),e._m(3),e._v(" "),e._m(4),e._v(" "),r("p",[e._v("然而,本章的目的在于学习,而不是走捷径。因为 Rust 是一个系统编程语言,我们能够选择处理什么层次的抽象,并能够选择比其他语言可能或可用的层次更低的层次。因此我们将自己编写一个基础的 HTTP server 和线程池,以便学习将来可能用到的 crate 背后的通用理念和技术。")])])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"最后的项目-构建多线程-web-server"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#最后的项目-构建多线程-web-server","aria-hidden":"true"}},[this._v("#")]),this._v(" 最后的项目: 构建多线程 web server")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("img",{attrs:{src:"img/trpl20-01.png",alt:"hello from rust"}})])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("图例 20-1: 我们最好将一起分享的项目")])])},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("ol",[r("li",[e._v("学习一些 TCP 与 HTTP 知识")]),e._v(" "),r("li",[e._v("在套接字(socket)上监听 TCP 请求")]),e._v(" "),r("li",[e._v("解析少量的 HTTP 请求")]),e._v(" "),r("li",[e._v("创建一个合适的 HTTP 响应")]),e._v(" "),r("li",[e._v("通过线程池改善 server 的吞吐量")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("不过在开始之前,需要提到一点:这里使用的方法并不是使用 Rust 构建 web server 最好的方法。"),t("em",[this._v("https://crates.io")]),this._v(" 上有很多可用于生产环境的 crate,它们提供了比我们所要编写的更为完整的 web server 和线程池实现。")])}],!1,null,null,null);n.options.__file="ch20-00-final-project-a-web-server.md";t.default=n.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/104.a030e309.js b/src/.vuepress/dist/assets/js/104.a030e309.js
new file mode 100644
index 0000000..60c47ed
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/104.a030e309.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[104],{245:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch20-01-single-threaded.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch20-01-single-threaded.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 90e6737d534cb66102674d183d2ef1966b190c2c")])]),t._v(" "),n("p",[t._v("首先让我们创建一个可运行的单线程 web server,不过在开始之前,我们将快速了解一下构建 web server 所涉及到的协议。这些协议的细节超出了本书的范畴,不过一个简单的概括会提供你所需的信息。")]),t._v(" "),t._m(1),t._v(" "),n("p",[t._v("TCP 是一个底层协议,它描述了信息如何从一个 server 到另一个的细节,不过其并不指定信息是什么。HTTP 构建于 TCP 之上,它定义了请求和响应的内容。为此,技术上讲可将 HTTP 用于其他协议之上,不过对于绝大部分情况,HTTP 通过 TCP 传输。我们将要做的就是处理 TCP 和 HTTP 请求与响应的原始字节数据。")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),n("p",[t._v("让我们试一试!启动程序并再次在浏览器中发起请求。注意浏览器中仍然会出现错误页面,不过终端中程序的输出现在看起来像这样:")]),t._v(" "),t._m(29),t._m(30),t._v(" "),n("p",[t._v("拆开请求数据来理解浏览器向程序请求了什么。")]),t._v(" "),t._m(31),t._v(" "),n("p",[t._v("HTTP 是一个基于文本的协议,同时一个请求有如下格式:")]),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._v(" "),n("p",[t._v("现在我们知道了浏览器请求了什么。让我们返回一些数据!")]),t._v(" "),t._m(39),t._v(" "),n("p",[t._v("我们将实现在客户端请求的响应中发送数据的功能。响应有如下格式:")]),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),n("p",[t._v("状态码 200 是一个标准的成功响应。这些文本是一个微型的成功 HTTP 响应。让我们将这些文本写入流作为成功请求的响应!")]),t._v(" "),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._v(" "),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._m(55),t._v(" "),t._m(56),t._v(" "),t._m(57),t._v(" "),t._m(58),t._m(59),t._v(" "),t._m(60),t._v(" "),t._m(61),t._v(" "),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._v(" "),t._m(65),t._v(" "),t._m(66),t._v(" "),t._m(67),t._m(68),t._v(" "),t._m(69),t._v(" "),t._m(70),t._v(" "),t._m(71),t._v(" "),t._m(72),t._v(" "),t._m(73),t._v(" "),t._m(74),t._m(75),t._v(" "),t._m(76),t._v(" "),t._m(77),t._v(" "),t._m(78),t._m(79),t._v(" "),t._m(80),t._v(" "),t._m(81),t._v(" "),t._m(82),t._v(" "),t._m(83),t._v(" "),t._m(84),t._m(85),t._v(" "),t._m(86),t._v(" "),t._m(87),t._v(" "),t._m(88),t._v(" "),n("p",[t._v("目前 server 运行于单线程中,它一次只能处理一个请求。让我们模拟一些慢请求来看看这如何会成为一个问题,并进行修复以便 server 可以一次处理多个请求。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"构建单线程-web-server"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#构建单线程-web-server","aria-hidden":"true"}},[this._v("#")]),this._v(" 构建单线程 web server")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("web server 中涉及到的两个主要协议是 "),n("strong",[t._v("超文本传输协议")]),t._v("("),n("em",[t._v("Hypertext Transfer Protocol")]),t._v(","),n("em",[t._v("HTTP")]),t._v(")和 "),n("strong",[t._v("传输控制协议")]),t._v("("),n("em",[t._v("Transmission Control Protocol")]),t._v(","),n("em",[t._v("TCP")]),t._v(")。这两者都是 "),n("strong",[t._v("请求-响应")]),t._v("("),n("em",[t._v("request-response")]),t._v(")协议,也就是说,有 "),n("strong",[t._v("客户端")]),t._v("("),n("em",[t._v("client")]),t._v(")来初始化请求,并有 "),n("strong",[t._v("服务端")]),t._v("("),n("em",[t._v("server")]),t._v(")监听请求并向客户端提供响应。请求与响应的内容由协议本身定义。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"监听-tcp-连接"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#监听-tcp-连接","aria-hidden":"true"}},[this._v("#")]),this._v(" 监听 TCP 连接")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("所以我们的 web server 所需做的第一件事便是能够监听 TCP 连接。标准库提供了 "),s("code",[this._v("std::net")]),this._v(" 模块处理这些功能。让我们一如既往新建一个项目:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo new hello --bin\n Created binary (application) `hello` project\n$ cd hello\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("并在 "),s("code",[this._v("src/main.rs")]),this._v(" 输入示例 20-1 中的代码作为开始。这段代码会在地址 "),s("code",[this._v("127.0.0.1:7878")]),this._v(" 上监听传入的 TCP 流。当获取到传入的流,它会打印出 "),s("code",[this._v("Connection established!")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,no_run extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('use std::net::TcpListener;\n\nfn main() {\n let listener = TcpListener::bind("127.0.0.1:7878").unwrap();\n\n for stream in listener.incoming() {\n let stream = stream.unwrap();\n\n println!("Connection established!");\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-1: 监听传入的流并在接收到流时打印信息")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("TcpListener")]),this._v(" 用于监听 TCP 连接。我们选择监听地址 "),s("code",[this._v("127.0.0.1:7878")]),this._v("。将这个地址拆开,冒号之前的部分是一个代表本机的 IP 地址(这个地址在每台计算机上都相同,并不特指作者的计算机),而 "),s("code",[this._v("7878")]),this._v(' 是端口。选择这个端口出于两个原因:通常 HTTP 接受这个端口而且 7878 在电话上打出来就是 "rust"(译者注:九宫格键盘上的英文)。注意连接 80 端口需要管理员权限;非管理员用户只能监听大于 1024 的端口。')])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在这个场景中 "),n("code",[t._v("bind")]),t._v(" 函数类似于 "),n("code",[t._v("new")]),t._v(" 函数,在这里它返回一个新的 "),n("code",[t._v("TcpListener")]),t._v(" 实例。这个函数叫做 "),n("code",[t._v("bind")]),t._v(" 是因为,在网络领域,连接到监听端口被称为 “绑定到一个端口”(“binding to a port”)")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("bind")]),this._v(" 函数返回 "),s("code",[this._v("Result")]),this._v(",这表明绑定可能会失败,例如,如果不是管理员尝试连接 80 端口,或是如果运行两个此程序的实例这样会有两个程序监听相同的端口,绑定会失败。因为我们是出于学习目的来编写一个基础的 server,将不用关心处理这类错误,使用 "),s("code",[this._v("unwrap")]),this._v(" 在出现这些情况时直接停止程序。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("TcpListener")]),t._v(" 的 "),n("code",[t._v("incoming")]),t._v(" 方法返回一个迭代器,它提供了一系列的流(更准确的说是 "),n("code",[t._v("TcpStream")]),t._v(" 类型的流)。"),n("strong",[t._v("流")]),t._v("("),n("em",[t._v("stream")]),t._v(")代表一个客户端和服务端之间打开的连接。"),n("strong",[t._v("连接")]),t._v("("),n("em",[t._v("connection")]),t._v(")代表客户端连接服务端、服务端生成响应以及服务端关闭连接的全部请求 / 响应过程。为此,"),n("code",[t._v("TcpStream")]),t._v(" 允许我们读取它来查看客户端发送了什么,并可以编写响应。总体来说,这个 "),n("code",[t._v("for")]),t._v(" 循环会依次处理每个连接并产生一系列的流供我们处理。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("目前为止,处理流的过程包含 "),n("code",[t._v("unwrap")]),t._v(" 调用,如果出现任何错误会终止程序,如果没有任何错误,则打印出信息。下一个示例我们将为成功的情况增加更多功能。当客户端连接到服务端时 "),n("code",[t._v("incoming")]),t._v(" 方法返回错误是可能的,因为我们实际上没有遍历连接,而是遍历 "),n("strong",[t._v("连接尝试")]),t._v("("),n("em",[t._v("connection attempts")]),t._v(")。连接可能会因为很多原因不能成功,大部分是操作系统相关的。例如,很多系统限制同时打开的连接数;新连接尝试产生错误,直到一些打开的连接关闭为止。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们试试这段代码!首先在终端执行 "),s("code",[this._v("cargo run")]),this._v(",接着在浏览器中加载 "),s("code",[this._v("127.0.0.1:7878")]),this._v("。浏览器会显示出看起来类似于“连接重置”(“Connection reset”)的错误信息,因为 server 目前并没响应任何数据。但是如果我们观察终端,会发现当浏览器连接 server 时会打印出一系列的信息!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v(" Running `target/debug/hello`\nConnection established!\nConnection established!\nConnection established!\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("有时会看到对于一次浏览器请求会打印出多条信息;这可能是因为浏览器在请求页面的同时还请求了其他资源,比如出现在浏览器 tab 标签中的 "),s("code",[this._v("favicon.ico")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这也可能是因为浏览器尝试多次连接 server,因为 server 没有响应任何数据。当 "),s("code",[this._v("stream")]),this._v(" 在循环的结尾离开作用域并被丢弃,其连接将被关闭,作为 "),s("code",[this._v("drop")]),this._v(" 实现的一部分。浏览器有时通过重连来处理关闭的连接,因为这些问题可能是暂时的。现在重要的是我们成功的处理了 TCP 连接!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("记得当运行完特定版本的代码后使用 "),s("span",{staticClass:"keystroke"},[this._v("ctrl-C")]),this._v(" 来停止程序,并在做出最新的代码修改之后执行 "),s("code",[this._v("cargo run")]),this._v(" 重启服务。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"读取请求"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#读取请求","aria-hidden":"true"}},[this._v("#")]),this._v(" 读取请求")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们实现读取来自浏览器请求的功能!为了分离获取连接和接下来对连接的操作的相关内容,我们将开始一个新函数来处理连接。在这个新的 "),s("code",[this._v("handle_connection")]),this._v(" 函数中,我们从 TCP 流中读取数据并打印出来以便观察浏览器发送过来的数据。将代码修改为如示例 20-2 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,no_run extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('use std::io::prelude::*;\nuse std::net::TcpListener;\nuse std::net::TcpStream;\n\nfn main() {\n let listener = TcpListener::bind("127.0.0.1:7878").unwrap();\n\n for stream in listener.incoming() {\n let stream = stream.unwrap();\n\n handle_connection(stream);\n }\n}\n\nfn handle_connection(mut stream: TcpStream) {\n let mut buffer = [0; 512];\n\n stream.read(&mut buffer).unwrap();\n\n println!("Request: {}", String::from_utf8_lossy(&buffer[..]));\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-2: 读取 "),s("code",[this._v("TcpStream")]),this._v(" 并打印数据")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里将 "),n("code",[t._v("std::io::prelude")]),t._v(" 引入作用域来获取读写流所需的特定 trait。在 "),n("code",[t._v("main")]),t._v(" 函数的 "),n("code",[t._v("for")]),t._v(" 循环中,相比获取到连接时打印信息,现在调用新的 "),n("code",[t._v("handle_connection")]),t._v(" 函数并向其传递 "),n("code",[t._v("stream")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 "),s("code",[this._v("handle_connection")]),this._v(" 中,"),s("code",[this._v("stream")]),this._v(" 参数是可变的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们将从流中读取数据,所以它需要是可修改的。这是因为 "),s("code",[this._v("TcpStream")]),this._v(" 实例在内部记录了所返回的数据。它可能读取了多于我们请求的数据并保存它们以备下一次请求数据。因此它需要是 "),s("code",[this._v("mut")]),this._v(" 的因为其内部状态可能会改变;通常我们认为 “读取” 不需要可变性,不过在这个例子中则需要 "),s("code",[this._v("mut")]),this._v(" 关键字。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接下来,需要实际读取流。这里分两步进行:首先,在栈上声明一个 "),s("code",[this._v("buffer")]),this._v(" 来存放读取到的数据。这里创建了一个 512 字节的缓冲区,它足以存放基本请求的数据并满足本章的目的需要。如果希望处理任意大小的请求,缓冲区管理将更为复杂,不过现在一切从简。接着将缓冲区传递给 "),s("code",[this._v("stream.read")]),this._v(" ,它会从 "),s("code",[this._v("TcpStream")]),this._v(" 中读取字节并放入缓冲区中。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("接下来将缓冲区中的字节转换为字符串并打印出来。"),n("code",[t._v("String::from_utf8_lossy")]),t._v(" 函数获取一个 "),n("code",[t._v("&[u8]")]),t._v(" 并产生一个 "),n("code",[t._v("String")]),t._v("。函数名的 “lossy” 部分来源于当其遇到无效的 UTF-8 序列时的行为:它使用 �,"),n("code",[t._v("U+FFFD REPLACEMENT CHARACTER")]),t._v(",来代替无效序列。你可能会在缓冲区的剩余部分看到这些替代字符,因为他们没有被请求数据填满。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling hello v0.1.0 (file:///projects/hello)\n Finished dev [unoptimized + debuginfo] target(s) in 0.42 secs\n Running `target/debug/hello`\nRequest: GET / HTTP/1.1\nHost: 127.0.0.1:7878\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101\nFirefox/52.0\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\nAccept-Language: en-US,en;q=0.5\nAccept-Encoding: gzip, deflate\nConnection: keep-alive\nUpgrade-Insecure-Requests: 1\n������������������������������������\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("根据使用的浏览器不同可能会出现稍微不同的数据。现在我们打印出了请求数据,可以通过观察 "),s("code",[this._v("Request: GET")]),this._v(" 之后的路径来解释为何会从浏览器得到多个连接。如果重复的连接都是请求 "),s("code",[this._v("/")]),this._v(",就知道了浏览器尝试重复获取 "),s("code",[this._v("/")]),this._v(" 因为它没有从程序得到响应。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"仔细观察-http-请求"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#仔细观察-http-请求","aria-hidden":"true"}},[this._v("#")]),this._v(" 仔细观察 hTTP 请求")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("Method Request-URI HTTP-Version CRLF\nheaders CRLF\nmessage-body\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("第一行叫做 "),n("strong",[t._v("请求行")]),t._v("("),n("em",[t._v("request line")]),t._v("),它存放了客户端请求了什么的信息。请求行的第一部分是所使用的 "),n("em",[t._v("method")]),t._v(",比如 "),n("code",[t._v("GET")]),t._v(" 或 "),n("code",[t._v("POST")]),t._v(",这描述了客户端如何进行请求。这里客户端使用了 "),n("code",[t._v("GET")]),t._v(" 请求。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Request")]),t._v(" 行接下来的部分是 "),n("code",[t._v("/")]),t._v(",它代表客户端请求的 "),n("strong",[t._v("统一资源标识符")]),t._v("("),n("em",[t._v("Uniform Resource Identifier")]),t._v(","),n("em",[t._v("URI")]),t._v(") —— URI 大体上类似,但也不完全类似于 URL("),n("strong",[t._v("统一资源定位符")]),t._v(","),n("em",[t._v("Uniform Resource Locators")]),t._v(")。URI 和 URL 之间的区别对于本章的目的来说并不重要,不过 HTTP 规范使用术语 URI,所以这里可以简单的将 URL 理解为 URI。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("最后,是客户端使用的 HTTP 版本,接着请求行以一个 CRLF 序列结尾。CRLF 序列也可以写作 "),n("code",[t._v("\\r\\n")]),t._v(":"),n("code",[t._v("\\r")]),t._v(" 是 "),n("strong",[t._v("回车")]),t._v("("),n("em",[t._v("carriage return")]),t._v(")而 "),n("code",[t._v("\\n")]),t._v(" 是 "),n("strong",[t._v("换行")]),t._v("("),n("em",[t._v("line feed")]),t._v(")(这些术语来自打字机时代!)。注意当 CRLF 被打印时,会看到开始了一个新行而不是 "),n("code",[t._v("\\r\\n")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("观察目前运行程序所接收到的数据的请求行,可以看到 "),s("code",[this._v("GET")]),this._v(" 是 method,"),s("code",[this._v("/")]),this._v(" 是请求 URI,而 "),s("code",[this._v("HTTP/1.1")]),this._v(" 是版本。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("从 "),s("code",[this._v("Host:")]),this._v(" 开始的其余的行是 headers;"),s("code",[this._v("GET")]),this._v(" 请求没有 body。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果你希望的话,尝试用不同的浏览器发送请求,或请求不同的地址,比如 "),s("code",[this._v("127.0.0.1:7878/test")]),this._v(",来观察请求数据如何变化。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"编写响应"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#编写响应","aria-hidden":"true"}},[this._v("#")]),this._v(" 编写响应")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("HTTP-Version Status-Code Reason-Phrase CRLF\nheaders CRLF\nmessage-body\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第一行叫做 "),s("strong",[this._v("状态行")]),this._v("("),s("em",[this._v("status line")]),this._v("),它包含响应的 HTTP 版本、一个数字状态码用以总结请求的结果和一个描述之前状态码的文本原因短语。CRLF 序列之后是任意 header,另一个 CRLF 序列,和响应的 body。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里是一个使用 HTTP 1.1 版本的响应例子,其状态码为 "),s("code",[this._v("200")]),this._v(",原因短语为 "),s("code",[this._v("OK")]),this._v(",没有 header,也没有 body:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("HTTP/1.1 200 OK\\r\\n\\r\\n\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 "),s("code",[this._v("handle_connection")]),this._v(" 函数中,我们需要去掉打印请求数据的 "),s("code",[this._v("println!")]),this._v(",并替换为示例 20-3 中的代码:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("prelude"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("net"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("handle_connection")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("512")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" response "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 200 OK\\r\\n\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("write")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("response"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("as_bytes")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("flush")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-3: 将一个微型成功 HTTP 响应写入流")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("新代码中的第一行定义了变量 "),n("code",[t._v("response")]),t._v(" 来存放将要返回的成功响应的数据。接着,在 "),n("code",[t._v("response")]),t._v(" 上调用 "),n("code",[t._v("as_bytes")]),t._v(",因为 "),n("code",[t._v("stream")]),t._v(" 的 "),n("code",[t._v("write")]),t._v(" 方法获取一个 "),n("code",[t._v("&[u8]")]),t._v(" 并直接将这些字节发送给连接。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("因为 "),n("code",[t._v("write")]),t._v(" 操作可能会失败,所以像之前那样对任何错误结果使用 "),n("code",[t._v("unwrap")]),t._v("。同理,在真实世界的应用中这里需要添加错误处理。最后,"),n("code",[t._v("flush")]),t._v(" 会等待并阻塞程序执行直到所有字节都被写入连接中;"),n("code",[t._v("TcpStream")]),t._v(" 包含一个内部缓冲区来最小化对底层操作系统的调用。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("有了这些修改,运行我们的代码并进行请求!我们不再向终端打印任何数据,所以不会再看到除了 Cargo 以外的任何输出。不过当在浏览器中加载 "),s("code",[this._v("127.0.0.1:7878")]),this._v(" 时,会得到一个空页面而不是错误。太棒了!我们刚刚手写了一个 HTTP 请求与响应。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"返回真正的-html"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#返回真正的-html","aria-hidden":"true"}},[this._v("#")]),this._v(" 返回真正的 HTML")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们实现不只是返回空页面的功能。在项目根目录创建一个新文件,"),s("em",[this._v("hello.html")]),this._v(" —— 也就是说,不是在 "),s("code",[this._v("src")]),this._v(" 目录。在此可以放入任何你期望的 HTML;列表 20-4 展示了一个可能的文本:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: hello.html")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-html extra-class"},[n("pre",{pre:!0,attrs:{class:"language-html"}},[n("code",[n("span",{attrs:{class:"token doctype"}},[t._v("")]),t._v("\n"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("html")]),t._v(" "),n("span",{attrs:{class:"token attr-name"}},[t._v("lang")]),n("span",{attrs:{class:"token attr-value"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("=")]),n("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("en"),n("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("head")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("meta")]),t._v(" "),n("span",{attrs:{class:"token attr-name"}},[t._v("charset")]),n("span",{attrs:{class:"token attr-value"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("=")]),n("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("utf-8"),n("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("title")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Hello!"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("title")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("head")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("body")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("h1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Hello!"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("h1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Hi from Rust"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("p")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("body")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("html")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-4: 一个简单的 HTML 文件用来作为响应")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这是一个极小化的 HTML 5 文档,它有一个标题和一小段文本。为了在 server 接受请求时返回它,需要如示例 20-5 所示修改 "),s("code",[this._v("handle_connection")]),this._v(" 来读取 HTML 文件,将其加入到响应的 body 中,并发送:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("prelude"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("net"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("File"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("handle_connection")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("512")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" file "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" File"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("open")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" contents "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n file"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read_to_string")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" response "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("format!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 200 OK\\r\\n\\r\\n{}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("write")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("response"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("as_bytes")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("flush")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-5: 将 "),s("em",[this._v("hello.html")]),this._v(" 的内容作为响应 body 发送")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在开头增加了一行来将标准库中的 "),s("code",[this._v("File")]),this._v(" 引入作用域。打开和读取文件的代码应该看起来很熟悉,因为第十二章 I/O 项目的示例 12-4 中读取文件内容时出现过类似的代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接下来,使用 "),s("code",[this._v("format!")]),this._v(" 将文件内容加入到将要写入流的成功响应的 body 中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用 "),s("code",[this._v("cargo run")]),this._v(" 运行程序,在浏览器加载 "),s("code",[this._v("127.0.0.1:7878")]),this._v(",你应该会看到渲染出来的 HTML 文件!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("目前忽略了 "),s("code",[this._v("buffer")]),this._v(" 中的请求数据并无条件的发送了 HTML 文件的内容。这意味着如果尝试在浏览器中请求 "),s("code",[this._v("127.0.0.1:7878/something-else")]),this._v(" 也会得到同样的 HTML 响应。如此其作用是非常有限的,也不是大部分 server 所做的;让我们检查请求并只对格式良好(well-formed)的请求 "),s("code",[this._v("/")]),this._v(" 发送 HTML 文件。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"验证请求并有选择的进行响应"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#验证请求并有选择的进行响应","aria-hidden":"true"}},[this._v("#")]),this._v(" 验证请求并有选择的进行响应")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("目前我们的 web server 不管客户端请求什么都会返回相同的 HTML 文件。让我们增加在返回 HTML 文件前检查浏览器是否请求 "),n("code",[t._v("/")]),t._v(",并在其请求任何其他内容时返回错误的功能。为此需要如示例 20-6 那样修改 "),n("code",[t._v("handle_connection")]),t._v("。新代码接收到的请求的内容与已知的 "),n("code",[t._v("/")]),t._v(" 请求的一部分做比较,并增加了 "),n("code",[t._v("if")]),t._v(" 和 "),n("code",[t._v("else")]),t._v(" 块来区别处理请求:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("prelude"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("net"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("File"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("handle_connection")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("512")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" get "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('b"GET / HTTP/1.1\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("starts_with")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("get"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" file "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" File"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("open")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" contents "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n file"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read_to_string")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" response "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("format!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 200 OK\\r\\n\\r\\n{}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("write")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("response"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("as_bytes")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("flush")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// some other request")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-6: 匹配请求并区别处理 "),s("code",[this._v("/")]),this._v(" 请求与其他请求")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("首先,将与 "),n("code",[t._v("/")]),t._v(" 请求相关的数据硬编码进变量 "),n("code",[t._v("get")]),t._v("。因为我们将原始字节读取进了缓冲区,所以在 "),n("code",[t._v("get")]),t._v(" 的数据开头增加 "),n("code",[t._v('b""')]),t._v(" 字节字符串语法将其转换为字节字符串。接着检查 "),n("code",[t._v("buffer")]),t._v(" 是否以 "),n("code",[t._v("get")]),t._v(" 中的字节开头。如果是,这就是一个格式良好的 "),n("code",[t._v("/")]),t._v(" 请求,也就是 "),n("code",[t._v("if")]),t._v(" 块中期望处理的成功情况,并会返回 HTML 文件内容的代码。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如果 "),n("code",[t._v("buffer")]),t._v(" "),n("strong",[t._v("不")]),t._v(" 以 "),n("code",[t._v("get")]),t._v(" 中的字节开头,就说明接收的是其他请求。之后会在 "),n("code",[t._v("else")]),t._v(" 块中增加代码来响应所有其他请求。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在如果运行代码并请求 "),s("code",[this._v("127.0.0.1:7878")]),this._v(",就会得到 "),s("em",[this._v("hello.html")]),this._v(" 中的 HTML。如果进行任何其他请求,比如 "),s("code",[this._v("127.0.0.1:7878/something-else")]),this._v(",则会得到像运行示例 20-1 和 20-2 中代码那样的连接错误。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在向示例 20-7 的 "),s("code",[this._v("else")]),this._v(" 块增加代码来返回一个带有 "),s("code",[this._v("404")]),this._v(" 状态码的响应,这代表了所请求的内容没有找到。接着也会返回一个 HTML 向浏览器终端用户表明此意:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("prelude"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("net"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("File"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("handle_connection")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("true")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" status_line "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 404 NOT FOUND\\r\\n\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" file "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" File"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("open")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"404.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" contents "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n file"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read_to_string")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" response "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("format!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"{}{}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" status_line"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("write")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("response"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("as_bytes")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("flush")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-7: 对于任何不是 "),s("code",[this._v("/")]),this._v(" 的请求返回 "),s("code",[this._v("404")]),this._v(" 状态码的响应和错误页面")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里,响应的状态行有状态码 "),n("code",[t._v("404")]),t._v(" 和原因短语 "),n("code",[t._v("NOT FOUND")]),t._v("。仍然没有返回任何 header,而其 body 将是 "),n("em",[t._v("404.html")]),t._v(" 文件中的 HTML。需要在 "),n("em",[t._v("hello.html")]),t._v(" 同级目录创建 "),n("em",[t._v("404.html")]),t._v(" 文件作为错误页面;这一次也可以随意使用任何 HTML 或使用示例 20-8 中的示例 HTML:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: 404.html")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-html extra-class"},[n("pre",{pre:!0,attrs:{class:"language-html"}},[n("code",[n("span",{attrs:{class:"token doctype"}},[t._v("")]),t._v("\n"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("html")]),t._v(" "),n("span",{attrs:{class:"token attr-name"}},[t._v("lang")]),n("span",{attrs:{class:"token attr-value"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("=")]),n("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("en"),n("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("head")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("meta")]),t._v(" "),n("span",{attrs:{class:"token attr-name"}},[t._v("charset")]),n("span",{attrs:{class:"token attr-value"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("=")]),n("span",{attrs:{class:"token punctuation"}},[t._v('"')]),t._v("utf-8"),n("span",{attrs:{class:"token punctuation"}},[t._v('"')])]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("title")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Hello!"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("title")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("head")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("body")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("h1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Oops!"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("h1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Sorry, I don't know what you're asking for."),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("p")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("body")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n"),n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token tag"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("")]),t._v("html")]),n("span",{attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-8: 任何 "),s("code",[this._v("404")]),this._v(" 响应所返回错误页面内容样例")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("有了这些修改,再次运行 server。请求 "),n("code",[t._v("127.0.0.1:7878")]),t._v(" 应该会返回 "),n("em",[t._v("hello.html")]),t._v(" 的内容,而对于任何其他请求,比如 "),n("code",[t._v("127.0.0.1:7878/foo")]),t._v(",应该会返回 "),n("em",[t._v("404.html")]),t._v(" 中的错误 HTML!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"少量代码重构"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#少量代码重构","aria-hidden":"true"}},[this._v("#")]),this._v(" 少量代码重构")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("目前 "),n("code",[t._v("if")]),t._v(" 和 "),n("code",[t._v("else")]),t._v(" 块中的代码有很多的重复:他们都读取文件并将其内容写入流。唯一的区别是状态行和文件名。为了使代码更为简明,将这些区别分别提取到一行 "),n("code",[t._v("if")]),t._v(" 和 "),n("code",[t._v("else")]),t._v(" 中,对状态行和文件名变量赋值;然后在读取文件和写入响应的代码中无条件的使用这些变量。重构后取代了大段 "),n("code",[t._v("if")]),t._v(" 和 "),n("code",[t._v("else")]),t._v(" 块代码后的结果如示例 20-9 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("prelude"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("net"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("File"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("handle_connection")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("512")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" get "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('b"GET / HTTP/1.1\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("status_line"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("starts_with")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("get"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 200 OK\\r\\n\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"hello.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 404 NOT FOUND\\r\\n\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"404.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" file "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" File"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("open")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" contents "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n file"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read_to_string")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" response "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("format!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"{}{}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" status_line"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("write")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("response"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("as_bytes")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("flush")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-9: 重构使得 "),s("code",[this._v("if")]),this._v(" 和 "),s("code",[this._v("else")]),this._v(" 块中只包含两个情况所不同的代码")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("现在 "),n("code",[t._v("if")]),t._v(" 和 "),n("code",[t._v("else")]),t._v(" 块所做的唯一的事就是在一个元组中返回合适的状态行和文件名的值;接着使用第十八章讲到的使用模式的 "),n("code",[t._v("let")]),t._v(" 语句通过解构元组的两部分为 "),n("code",[t._v("filename")]),t._v(" 和 "),n("code",[t._v("header")]),t._v(" 赋值。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("之前读取文件和写入响应的冗余代码现在位于 "),n("code",[t._v("if")]),t._v(" 和 "),n("code",[t._v("else")]),t._v(" 块之外,并会使用变量 "),n("code",[t._v("status_line")]),t._v(" 和 "),n("code",[t._v("filename")]),t._v("。这样更易于观察这两种情况真正有何不同,还意味着如果需要改变如何读取文件或写入响应时只需要更新一处的代码。示例 20-9 中代码的行为与示例 20-8 完全一样。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("好极了!我们有了一个 40 行左右 Rust 代码的小而简单的 server,它对一个请求返回页面内容而对所有其他请求返回 "),s("code",[this._v("404")]),this._v(" 响应。")])}],!1,null,null,null);e.options.__file="ch20-01-single-threaded.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/105.ceb5b0f7.js b/src/.vuepress/dist/assets/js/105.ceb5b0f7.js
new file mode 100644
index 0000000..045c8dc
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/105.ceb5b0f7.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[105],{244:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch20-02-multithreaded.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch20-02-multithreaded.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 1f0136399ba2f5540ecc301fab04bd36492e5554")])]),t._v(" "),n("p",[t._v("目前 server 会依次处理每一个请求,意味着它在完成第一个连接的处理之前不会处理第二个连接。如果 server 正接收越来越多的请求,这类串行操作会使性能越来越差。如果一个请求花费很长时间来处理,随后而来的请求则不得不等待这个长请求结束,即便这些新请求可以很快就处理完。我们需要修复这种情况,不过首先让我们实际尝试一下这个问题。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),n("p",[t._v("现在就可以真切的看出我们的 server 有多么的原始;真实的库将会以更简洁的方式处理多请求识别问题。")]),t._v(" "),t._m(7),t._v(" "),n("p",[t._v("这里有多种办法来改变我们的 web server 使其避免所有请求都排在慢请求之后;我们将要实现的一个便是线程池。")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),n("p",[t._v("我们会将池中线程限制为较少的数量,以防拒绝服务(Denial of Service, DoS)攻击;如果程序为每一个接收的请求都新建一个线程,某人向 server 发起千万级的请求时会耗尽服务器的资源并导致所有请求的处理都被终止。")]),t._v(" "),t._m(10),t._v(" "),n("p",[t._v("这个设计仅仅是多种改善 web server 吞吐量的方法之一。其他可供探索的方法有 fork/join 模型和单线程异步 I/O 模型。如果你对这个主题感兴趣,则可以阅读更多关于其他解决方案的内容并尝试用 Rust 实现他们;对于一个像 Rust 这样的底层语言,所有这些方法都是可能的。")]),t._v(" "),n("p",[t._v("在开始之前,让我们讨论一下线程池应用看起来怎样。当尝试设计代码时,首先编写客户端接口确实有助于指导代码设计。以期望的调用方式来构建 API 代码的结构,接着在这个结构之内实现功能,而不是先实现功能再设计公有 API。")]),t._v(" "),n("p",[t._v("类似于第十二章项目中使用的测试驱动开发。这里将要使用编译器驱动开发(Compiler Driven Development)。我们将编写调用所期望的函数的代码,接着观察编译器错误告诉我们接下来需要修改什么使得代码可以工作。")]),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),n("p",[t._v("这仍然不能工作,再次尝试运行来得到下一个需要解决的错误:")]),t._v(" "),t._m(33),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._m(37),t._v(" "),n("p",[t._v("再次编译检查这段代码:")]),t._v(" "),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._m(49),t._v(" "),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),t._v(" "),t._m(56),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._v(" "),t._m(60),t._m(61),t._v(" "),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._m(65),t._v(" "),t._m(66),t._v(" "),t._m(67),t._v(" "),t._m(68),t._m(69),t._v(" "),t._m(70),t._v(" "),t._m(71),t._v(" "),t._m(72),t._v(" "),t._m(73),t._v(" "),t._m(74),t._v(" "),t._m(75),t._v(" "),t._m(76),t._v(" "),t._m(77),t._v(" "),t._m(78),t._v(" "),n("p",[t._v("如果你渴望挑战,在查示例 20-15 中的代码之前尝试自己实现这些修改。")]),t._v(" "),n("p",[t._v("准备好了吗?示例 20-15 就是一个做出了这些修改的例子:")]),t._v(" "),t._m(79),t._v(" "),t._m(80),t._m(81),t._v(" "),t._m(82),t._v(" "),t._m(83),t._v(" "),t._m(84),t._v(" "),t._m(85),t._v(" "),t._m(86),t._v(" "),t._m(87),t._v(" "),t._m(88),t._v(" "),t._m(89),t._v(" "),t._m(90),t._v(" "),t._m(91),t._v(" "),t._m(92),t._m(93),t._v(" "),t._m(94),t._v(" "),t._m(95),t._v(" "),t._m(96),t._v(" "),t._m(97),t._m(98),t._v(" "),t._m(99),t._v(" "),n("p",[t._v("如果尝试 check 代码,会得到这个错误:")]),t._v(" "),t._m(100),t._m(101),t._v(" "),t._m(102),t._v(" "),t._m(103),t._v(" "),t._m(104),t._v(" "),t._m(105),t._m(106),t._v(" "),t._m(107),t._v(" "),n("p",[t._v("通过这些修改,代码可以编译了!我们做到了!")]),t._v(" "),t._m(108),t._v(" "),t._m(109),t._v(" "),t._m(110),t._v(" "),t._m(111),t._m(112),t._v(" "),t._m(113),t._v(" "),t._m(114),t._v(" "),t._m(115),t._v(" "),t._m(116),t._m(117),t._v(" "),t._m(118),t._v(" "),t._m(119),t._v(" "),t._m(120),t._v(" "),n("p",[t._v("理论上这段代码应该能够编译。不幸的是,Rust 编译器仍不够完美,会给出如下错误:")]),t._v(" "),t._m(121),t._m(122),t._v(" "),t._m(123),t._v(" "),n("p",[t._v("Rust 仍在努力改进提升编译器的过程中,不过将来示例 20-20 中的代码应该能够正常工作。有很多像你一样的人正在修复这个以及其他问题!当你结束了本书的阅读,我们希望看到你也成为他们中的一员。")]),t._v(" "),t._m(124),t._v(" "),t._m(125),t._v(" "),t._m(126),t._m(127),t._v(" "),t._m(128),t._v(" "),t._m(129),t._v(" "),t._m(130),t._v(" "),t._m(131),t._v(" "),n("p",[t._v("这是非常狡猾且复杂的手段。无需过分担心他们并不是非常有道理;总有一天,这一切将是毫无必要的。")]),t._v(" "),t._m(132),t._v(" "),t._m(133),t._m(134),t._v(" "),t._m(135),t._v(" "),t._m(136),t._v(" "),t._m(137),t._m(138),t._v(" "),t._m(139),t._v(" "),t._m(140)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"将单线程-server-变为多线程-server"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#将单线程-server-变为多线程-server","aria-hidden":"true"}},[this._v("#")]),this._v(" 将单线程 server 变为多线程 server")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"在当前-server-实现中模拟慢请求"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#在当前-server-实现中模拟慢请求","aria-hidden":"true"}},[this._v("#")]),this._v(" 在当前 server 实现中模拟慢请求")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们看看一个慢请求如何影响当前 server 实现中的其他请求。示例 20-10 通过模拟慢响应实现了 "),s("code",[this._v("/sleep")]),this._v(" 请求处理,它会使 server 在响应之前休眠五秒。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("time"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("prelude"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("net"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("File"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("handle_connection")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("512")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" get "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('b"GET / HTTP/1.1\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" sleep "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('b"GET /sleep HTTP/1.1\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("status_line"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("starts_with")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("get"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 200 OK\\r\\n\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"hello.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("starts_with")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sleep"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("sleep")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from_secs")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 200 OK\\r\\n\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"hello.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 404 NOT FOUND\\r\\n\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"404.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-10: 通过识别 "),s("code",[this._v("/sleep")]),this._v(" 并休眠五秒来模拟慢请求")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这段代码有些凌乱,不过对于模拟的目的来说已经足够!这里创建了第二个请求 "),n("code",[t._v("sleep")]),t._v(",我们会识别其数据。在 "),n("code",[t._v("if")]),t._v(" 块之后增加了一个 "),n("code",[t._v("else if")]),t._v(" 来检查 "),n("code",[t._v("/sleep")]),t._v(" 请求,当接收到这个请求时,在渲染成功 HTML 页面之前会先休眠五秒。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("使用 "),n("code",[t._v("cargo run")]),t._v(" 启动 server,并接着打开两个浏览器窗口:一个请求 "),n("code",[t._v("http://localhost:7878/")]),t._v(" 而另一个请求 "),n("code",[t._v("http://localhost:7878/sleep")]),t._v("。如果像之前一样多次请求 "),n("code",[t._v("/")]),t._v(",会发现响应的比较快速。不过如果请求"),n("code",[t._v("/sleep")]),t._v(" 之后在请求 "),n("code",[t._v("/")]),t._v(",就会看到 "),n("code",[t._v("/")]),t._v(" 会等待直到 "),n("code",[t._v("sleep")]),t._v(" 休眠完五秒之后才出现。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用线程池改善吞吐量"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用线程池改善吞吐量","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用线程池改善吞吐量")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("线程池")]),this._v("("),s("em",[this._v("thread pool")]),this._v(")是一组预先分配的等待或准备处理任务的线程。当程序收到一个新任务,线程池中的一个线程会被分配任务,这个线程会离开并处理任务。其余的线程则可用于处理在第一个线程处理任务的同时处理其他接收到的任务。当第一个线程处理完任务时,它会返回空闲线程池中等待处理新任务。线程池允许我们并发处理连接,增加 server 的吞吐量。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不同于分配无限的线程,线程池中将有固定数量的等待线程。当新进请求时,将请求发送到线程池中做处理。线程池会维护一个接收请求的队列。每一个线程会从队列中取出一个请求,处理请求,接着向对队列索取另一个请求。通过这种设计,则可以并发处理 "),s("code",[this._v("N")]),this._v(" 个请求,其中 "),s("code",[this._v("N")]),this._v(" 为线程数。如果每一个线程都在响应慢请求,之后的请求仍然会阻塞队列,不过相比之前增加了能处理的慢请求的数量。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"为每一个请求分配线程的代码结构"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#为每一个请求分配线程的代码结构","aria-hidden":"true"}},[this._v("#")]),this._v(" 为每一个请求分配线程的代码结构")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("首先,让我们探索一下为每一个连接都创建一个线程的代码看起来如何。这并不是最终方案,因为正如之前讲到的它会潜在的分配无限的线程,不过这是一个开始。示例 20-11 展示了 "),s("code",[this._v("main")]),this._v(" 的改变,它在 "),s("code",[this._v("for")]),this._v(" 循环中为每一个流分配了一个新线程进行处理:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,no_run extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('# use std::thread;\n# use std::io::prelude::*;\n# use std::net::TcpListener;\n# use std::net::TcpStream;\n#\nfn main() {\n let listener = TcpListener::bind("127.0.0.1:7878").unwrap();\n\n for stream in listener.incoming() {\n let stream = stream.unwrap();\n\n thread::spawn(|| {\n handle_connection(stream);\n });\n }\n}\n# fn handle_connection(mut stream: TcpStream) {}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-11: 为每一个流新建一个线程")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("正如第十六章讲到的,"),n("code",[t._v("thread::spawn")]),t._v(" 会创建一个新线程并在其中运行闭包中的代码。如果运行这段代码并在在浏览器中加载 "),n("code",[t._v("/sleep")]),t._v(",接着在另两个浏览器标签页中加载 "),n("code",[t._v("/")]),t._v(",确实会发现 "),n("code",[t._v("/")]),t._v(" 请求不必等待 "),n("code",[t._v("/sleep")]),t._v(" 结束。不过正如之前提到的,这最终会使系统崩溃因为我们无限制的创建新线程。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"为有限数量的线程创建一个类似的接口"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#为有限数量的线程创建一个类似的接口","aria-hidden":"true"}},[this._v("#")]),this._v(" 为有限数量的线程创建一个类似的接口")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们期望线程池以类似且熟悉的方式工作,以便从线程切换到线程池并不会对使用该 API 的代码做出较大的修改。示例 20-12 展示我们希望用来替换 "),s("code",[this._v("thread::spawn")]),this._v(" 的 "),s("code",[this._v("ThreadPool")]),this._v(" 结构体的假想接口:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,no_run extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('# use std::thread;\n# use std::io::prelude::*;\n# use std::net::TcpListener;\n# use std::net::TcpStream;\n# struct ThreadPool;\n# impl ThreadPool {\n# fn new(size: u32) -> ThreadPool { ThreadPool }\n# fn execute(&self, f: F)\n# where F: FnOnce() + Send + \'static {}\n# }\n#\nfn main() {\n let listener = TcpListener::bind("127.0.0.1:7878").unwrap();\n let pool = ThreadPool::new(4);\n\n for stream in listener.incoming() {\n let stream = stream.unwrap();\n\n pool.execute(|| {\n handle_connection(stream);\n });\n }\n}\n# fn handle_connection(mut stream: TcpStream) {}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-12: 假想的 "),s("code",[this._v("ThreadPool")]),this._v(" 接口")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里使用 "),n("code",[t._v("ThreadPool::new")]),t._v(" 来创建一个新的线程池,它有一个可配置的线程数的参数,在这里是四。这样在 "),n("code",[t._v("for")]),t._v(" 循环中,"),n("code",[t._v("pool.execute")]),t._v(" 有着类似 "),n("code",[t._v("thread::spawn")]),t._v(" 的接口,它获取一个线程池运行于每一个流的闭包。"),n("code",[t._v("pool.execute")]),t._v(" 需要实现为获取闭包并传递给池中的线程运行。这段代码还不能编译,不过通过尝试编译器会指导我们如何修复它。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"采用编译器驱动构建-threadpool-结构体"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#采用编译器驱动构建-threadpool-结构体","aria-hidden":"true"}},[this._v("#")]),this._v(" 采用编译器驱动构建 "),s("code",[this._v("ThreadPool")]),this._v(" 结构体")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("继续并对示例 20-12 中的 "),s("em",[this._v("src/main.rs")]),this._v(" 做出修改,并利用来自 "),s("code",[this._v("cargo check")]),this._v(" 的编译器错误来驱动开发。下面是我们得到的第一个错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nerror[E0433]: failed to resolve. Use of undeclared type or module `ThreadPool`\n --\x3e src\\main.rs:10:16\n |\n10 | let pool = ThreadPool::new(4);\n | ^^^^^^^^^^^^^^^ Use of undeclared type or module\n `ThreadPool`\n\nerror: aborting due to previous error\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("好的,这告诉我们需要一个 "),n("code",[t._v("ThreadPool")]),t._v(" 类型或模块,所以我们将构建一个。"),n("code",[t._v("ThreadPool")]),t._v(" 的实现会与 web server 的特定工作相独立,所以让我们从 "),n("code",[t._v("hello")]),t._v(" crate 切换到存放 "),n("code",[t._v("ThreadPool")]),t._v(" 实现的新库 crate。这也意味着可以在任何工作中使用这个单独的线程池库,而不仅仅是处理网络请求。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("创建 "),s("em",[this._v("src/lib.rs")]),this._v(" 文件,它包含了目前可用的最简单的 "),s("code",[this._v("ThreadPool")]),this._v(" 定义:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[this._v("pub")]),this._v(" "),s("span",{attrs:{class:"token keyword"}},[this._v("struct")]),this._v(" ThreadPool"),s("span",{attrs:{class:"token punctuation"}},[this._v(";")]),this._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("接着创建一个新目录,"),n("em",[t._v("src/bin")]),t._v(",并将二进制 crate 根文件从 "),n("em",[t._v("src/main.rs")]),t._v(" 移动到 "),n("em",[t._v("src/bin/main.rs")]),t._v("。这使得库 crate 成为 "),n("em",[t._v("hello")]),t._v(" 目录的主要 crate;不过仍然可以使用 "),n("code",[t._v("cargo run")]),t._v(" 运行 "),n("em",[t._v("src/bin/main.rs")]),t._v(" 二进制文件。移动了 "),n("em",[t._v("main.rs")]),t._v(" 文件之后,修改 "),n("em",[t._v("src/bin/main.rs")]),t._v(" 文件开头加入如下代码来引入库 crate 并将 "),n("code",[t._v("ThreadPool")]),t._v(" 引入作用域:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/bin/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("extern crate hello;\nuse hello::ThreadPool;\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nerror[E0599]: no function or associated item named `new` found for type\n`hello::ThreadPool` in the current scope\n --\x3e src/bin/main.rs:13:16\n |\n13 | let pool = ThreadPool::new(4);\n | ^^^^^^^^^^^^^^^ function or associated item not found in\n `hello::ThreadPool`\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("好的,这告诉我们下一步是为 "),n("code",[t._v("ThreadPool")]),t._v(" 创建一个叫做 "),n("code",[t._v("new")]),t._v(" 的关联函数。我们还知道 "),n("code",[t._v("new")]),t._v(" 需要有一个参数可以接受 "),n("code",[t._v("4")]),t._v(",而且 "),n("code",[t._v("new")]),t._v(" 应该返回 "),n("code",[t._v("ThreadPool")]),t._v(" 实例。让我们实现拥有此特征的最小化 "),n("code",[t._v("new")]),t._v(" 函数:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件夹: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ThreadPool\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里选择 "),s("code",[this._v("usize")]),this._v(" 作为 "),s("code",[this._v("size")]),this._v(" 参数的类型,因为我们知道为负的线程数没有意义。我们还知道将使用 4 作为线程集合的元素数量,这也就是使用 "),s("code",[this._v("usize")]),this._v(" 类型的原因,如第三章 “整数类型” 部分所讲。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nwarning: unused variable: `size`\n --\x3e src/lib.rs:4:16\n |\n4 | pub fn new(size: usize) -> ThreadPool {\n | ^^^^\n |\n = note: #[warn(unused_variables)] on by default\n = note: to avoid this warning, consider using `_size` instead\n\nerror[E0599]: no method named `execute` found for type `hello::ThreadPool` in the current scope\n --\x3e src/bin/main.rs:18:14\n |\n18 | pool.execute(|| {\n | ^^^^^^^\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("现在有了一个警告和一个错误。暂时先忽略警告,发生错误是因为并没有 "),n("code",[t._v("ThreadPool")]),t._v(" 上的 "),n("code",[t._v("execute")]),t._v(" 方法。回忆 “为有限数量的线程创建一个类似的接口” 部分我们决定线程池应该有与 "),n("code",[t._v("thread::spawn")]),t._v(" 类似的接口,同时我们将实现 "),n("code",[t._v("execute")]),t._v(" 函数来获取传递的闭包并将其传递给池中的空闲线程执行。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们会在 "),n("code",[t._v("ThreadPool")]),t._v(" 上定义 "),n("code",[t._v("execute")]),t._v(" 函数来获取一个闭包参数。回忆第十三章的 “使用带有泛型和 "),n("code",[t._v("Fn")]),t._v(" trait 的闭包” 部分,闭包作为参数时可以使用三个不同的 trait:"),n("code",[t._v("Fn")]),t._v("、"),n("code",[t._v("FnMut")]),t._v(" 和 "),n("code",[t._v("FnOnce")]),t._v("。我们需要决定这里应该使用哪种闭包。最终需要实现的类似于标准库的 "),n("code",[t._v("thread::spawn")]),t._v(",所以我们可以观察 "),n("code",[t._v("thread::spawn")]),t._v(" 的签名在其参数中使用了何种 bound。查看文档会发现:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("pub fn spawn(f: F) -> JoinHandle\n where\n F: FnOnce() -> T + Send + 'static,\n T: Send + 'static\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("F")]),t._v(" 是这里我们关心的参数;"),n("code",[t._v("T")]),t._v(" 与返回值有关所以我们并不关心。考虑到 "),n("code",[t._v("spawn")]),t._v(" 使用 "),n("code",[t._v("FnOnce")]),t._v(" 作为 "),n("code",[t._v("F")]),t._v(" 的 trait bound,这可能也是我们需要的,因为最终会将传递给 "),n("code",[t._v("execute")]),t._v(" 的参数传给 "),n("code",[t._v("spawn")]),t._v("。因为处理请求的线程只会执行闭包一次,这也进一步确认了 "),n("code",[t._v("FnOnce")]),t._v(" 是我们需要的 trait,这里符合 "),n("code",[t._v("FnOnce")]),t._v(" 中 "),n("code",[t._v("Once")]),t._v(" 的意思。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("F")]),t._v(" 还有 trait bound "),n("code",[t._v("Send")]),t._v(" 和生命周期绑定 "),n("code",[t._v("'static")]),t._v(",这对我们的情况也是有意义的:需要 "),n("code",[t._v("Send")]),t._v(" 来将闭包从一个线程转移到另一个线程,而 "),n("code",[t._v("'static")]),t._v(" 是因为并不知道线程会执行多久。让我们编写一个使用带有这些 bound 的泛型参数 "),n("code",[t._v("F")]),t._v(" 的 "),n("code",[t._v("ThreadPool")]),t._v(" 的 "),n("code",[t._v("execute")]),t._v(" 方法:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" execute"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" F"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v("\n F"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("FnOnce")]),this._v(" trait 仍然需要之后的 "),s("code",[this._v("()")]),this._v(",因为这里的 "),s("code",[this._v("FnOnce")]),this._v(" 代表一个没有参数也没有返回值的闭包。正如函数的定义,返回值类型可以从签名中省略,不过即便没有参数也需要括号。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里再一次增加了 "),s("code",[this._v("execute")]),this._v(" 方法的最小化实现,它没有做任何工作。再次进行检查:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nwarning: unused variable: `size`\n --\x3e src/lib.rs:4:16\n |\n4 | pub fn new(size: usize) -> ThreadPool {\n | ^^^^\n |\n = note: #[warn(unused_variables)] on by default\n = note: to avoid this warning, consider using `_size` instead\n\nwarning: unused variable: `f`\n --\x3e src/lib.rs:8:30\n |\n8 | pub fn execute(&self, f: F)\n | ^\n |\n = note: to avoid this warning, consider using `_f` instead\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在就只有警告了!这意味着能够编译了!注意如果尝试 "),s("code",[this._v("cargo run")]),this._v(" 运行程序并在浏览器中发起请求,仍会在浏览器中出现在本章开始时那样的错误。这个库实际上还没有调用传递给 "),s("code",[this._v("execute")]),this._v(" 的闭包!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("blockquote",[s("p",[this._v("一个你可能听说过的关于像 Haskell 和 Rust 这样有严格编译器的语言的说法是 “如果代码能够编译,它就能工作”。这是一个提醒大家的好时机,实际上这并不是普适的。我们的项目可以编译,不过它完全没有做任何工作!如果构建一个真实且功能完整的项目,则需花费大量的时间来开始编写单元测试来检查代码能否编译 "),s("strong",[this._v("并且")]),this._v(" 拥有期望的行为。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"在-new-中验证池中线程数量"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#在-new-中验证池中线程数量","aria-hidden":"true"}},[this._v("#")]),this._v(" 在 "),s("code",[this._v("new")]),this._v(" 中验证池中线程数量")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里仍然存在警告是因为其并没有对 "),s("code",[this._v("new")]),this._v(" 和 "),s("code",[this._v("execute")]),this._v(" 的参数做任何操作。让我们用期望的行为来实现这些函数。以考虑 "),s("code",[this._v("new")]),this._v(" 作为开始。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("之前选择使用无符号类型作为 "),n("code",[t._v("size")]),t._v(" 参数的类型,因为线程数为负的线程池没有意义。然而,线程数为零的线程池同样没有意义,不过零是一个完全有效的 "),n("code",[t._v("u32")]),t._v(" 值。让我们增加在返回 "),n("code",[t._v("ThreadPool")]),t._v(" 实例之前检查 "),n("code",[t._v("size")]),t._v(" 是否大于零的代码,并使用 "),n("code",[t._v("assert!")]),t._v(" 宏在得到零时 panic,如示例 20-13 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在返回 "),s("code",[this._v("ThreadPool")]),this._v(" 之前检查 "),s("code",[this._v("size")]),this._v(" 是否大于零,并使用 "),s("code",[this._v("assert!")]),this._v(" 宏在得到零时 panic,如列表 20-13 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("/// Create a new ThreadPool.")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("/// The size is the number of threads in the pool.")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("/// # Panics")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("/// The `new` function will panic if the size is zero.")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n ThreadPool\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-13: 实现 "),s("code",[this._v("ThreadPool::new")]),this._v(" 在 "),s("code",[this._v("size")]),this._v(" 为零时 panic")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("趁着这个机会我们用文档注释为 "),n("code",[t._v("ThreadPool")]),t._v(" 增加了一些文档。注意这里遵循了良好的文档实践并增加了一个部分来提示函数会 panic 的情况,正如第十四章所讨论的。尝试运行 "),n("code",[t._v("cargo doc --open")]),t._v(" 并点击 "),n("code",[t._v("ThreadPool")]),t._v(" 结构体来查看生成的 "),n("code",[t._v("new")]),t._v(" 的文档看起来如何!")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("相比像这里使用 "),n("code",[t._v("assert!")]),t._v(" 宏,也可以让 "),n("code",[t._v("new")]),t._v(" 像之前 I/O 项目中示例 12-9 中 "),n("code",[t._v("Config::new")]),t._v(" 那样返回一个 "),n("code",[t._v("Result")]),t._v(",不过在这里我们选择创建一个没有任何线程的线程池应该是不可恢复的错误。如果你想做的更好,尝试编写一个采用如下签名的 "),n("code",[t._v("new")]),t._v(" 版本来感受一下两者的区别:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn new(size: usize) -> Result {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"分配空间以储存线程"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#分配空间以储存线程","aria-hidden":"true"}},[this._v("#")]),this._v(" 分配空间以储存线程")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在有了一个有效的线程池线程数,就可以实际创建这些线程并在返回之前将他们储存在 "),s("code",[this._v("ThreadPool")]),this._v(" 结构体中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这引出了另一个问题:如何 “储存” 一个线程?让我们再看看 "),s("code",[this._v("thread::spawn")]),this._v(" 的签名:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("pub fn spawn(f: F) -> JoinHandle\n where\n F: FnOnce() -> T + Send + 'static,\n T: Send + 'static\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("spawn")]),t._v(" 返回 "),n("code",[t._v("JoinHandle")]),t._v(",其中 "),n("code",[t._v("T")]),t._v(" 是闭包返回的类型。尝试使用 "),n("code",[t._v("JoinHandle")]),t._v(" 来看看会发生什么。在我们的情况中,传递给线程池的闭包会处理连接并不返回任何值,所以 "),n("code",[t._v("T")]),t._v(" 将会是单元类型 "),n("code",[t._v("()")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 20-14 中的代码可以编译,不过实际上还并没有创建任何线程。我们改变了 "),n("code",[t._v("ThreadPool")]),t._v(" 的定义来存放一个 "),n("code",[t._v("thread::JoinHandle<()>")]),t._v(" 的 vector 实例,使用 "),n("code",[t._v("size")]),t._v(" 容量来初始化,并设置一个 "),n("code",[t._v("for")]),t._v(" 循环了来运行创建线程的代码,并返回包含这些线程的 "),n("code",[t._v("ThreadPool")]),t._v(" 实例:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("use std::thread;\n\npub struct ThreadPool {\n threads: Vec>,\n}\n\nimpl ThreadPool {\n // --snip--\n pub fn new(size: usize) -> ThreadPool {\n assert!(size > 0);\n\n let mut threads = Vec::with_capacity(size);\n\n for _ in 0..size {\n // create some threads and store them in the vector\n }\n\n ThreadPool {\n threads\n }\n }\n\n // --snip--\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-14: 为 "),s("code",[this._v("ThreadPool")]),this._v(" 创建一个 vector 来存放线程")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里将 "),s("code",[this._v("std::thread")]),this._v(" 引入库 crate 的作用域,因为使用了 "),s("code",[this._v("thread::JoinHandle")]),this._v(" 作为 "),s("code",[this._v("ThreadPool")]),this._v(" 中 vector 元素的类型。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在得到了有效的数量之后,"),n("code",[t._v("ThreadPool")]),t._v(" 新建一个存放 "),n("code",[t._v("size")]),t._v(" 个元素的 vector。本书还未使用过 "),n("code",[t._v("with_capacity")]),t._v(",它与 "),n("code",[t._v("Vec::new")]),t._v(" 做了同样的工作,不过有一个重要的区别:它为 vector 预先分配空间。因为已经知道了 vector 中需要 "),n("code",[t._v("size")]),t._v(" 个元素,预先进行分配比仅仅 "),n("code",[t._v("Vec::new")]),t._v(" 要稍微有效率一些,因为 "),n("code",[t._v("Vec::new")]),t._v(" 随着插入元素而重新改变大小。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果再次运行 "),s("code",[this._v("cargo check")]),this._v(",会看到一些警告,不过应该可以编译成功。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"worker-结构体负责从-threadpool-中将代码传递给线程"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#worker-结构体负责从-threadpool-中将代码传递给线程","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("Worker")]),this._v(" 结构体负责从 "),s("code",[this._v("ThreadPool")]),this._v(" 中将代码传递给线程")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 20-14 的 "),s("code",[this._v("for")]),this._v(" 循环中留下了一个关于创建线程的注释。如何实际创建线程呢?这是一个难题。标准库提供的创建线程的方法,"),s("code",[this._v("thread::spawn")]),this._v(",它期望获取一些一旦创建线程就应该执行的代码。然而,我们希望开始线程并使其等待稍后传递的代码。标准库的线程实现并没有包含这么做的方法;我们必须自己实现。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们将要实现的行为是创建线程并稍后发送代码,这会在 "),s("code",[this._v("ThreadPool")]),this._v(" 和线程间引入一个新数据类型来管理这种新行为。这个数据结构称为 "),s("code",[this._v("Worker")]),this._v(":这是一个池实现中的常见概念。想象一下在餐馆厨房工作的员工:员工等待来自客户的订单,他们负责接受这些订单并完成它们。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("不同于在线程池中储存一个 "),n("code",[t._v("JoinHandle<()>")]),t._v(" 实例的 vector,我们会储存 "),n("code",[t._v("Worker")]),t._v(" 结构体的实例。每一个 "),n("code",[t._v("Worker")]),t._v(" 会储存一个单独的 "),n("code",[t._v("JoinHandle<()>")]),t._v(" 实例。接着会在\n"),n("code",[t._v("Worker")]),t._v(" 上实现一个方法,它会获取需要允许代码的闭包并将其发送给已经运行的线程执行。我们还会赋予每一个 worker "),n("code",[t._v("id")]),t._v(",这样就可以在日志和调试中区别线程池中的不同 worker。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("首先,让我们做出如此创建 "),s("code",[this._v("ThreadPool")]),this._v(" 时所需的修改。在通过如下方式设置完 "),s("code",[this._v("Worker")]),this._v(" 之后,我们会实现向线程发送闭包的代码:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ol",[n("li",[t._v("定义 "),n("code",[t._v("Worker")]),t._v(" 结构体存放 "),n("code",[t._v("id")]),t._v(" 和 "),n("code",[t._v("JoinHandle<()>")])]),t._v(" "),n("li",[t._v("修改 "),n("code",[t._v("ThreadPool")]),t._v(" 存放一个 "),n("code",[t._v("Worker")]),t._v(" 实例的 vector")]),t._v(" "),n("li",[t._v("定义 "),n("code",[t._v("Worker::new")]),t._v(" 函数,它获取一个 "),n("code",[t._v("id")]),t._v(" 数字并返回一个带有 "),n("code",[t._v("id")]),t._v(" 和用空闭包分配的线程的 "),n("code",[t._v("Worker")]),t._v(" 实例")]),t._v(" "),n("li",[t._v("在 "),n("code",[t._v("ThreadPool::new")]),t._v(" 中,使用 "),n("code",[t._v("for")]),t._v(" 循环计数生成 "),n("code",[t._v("id")]),t._v(",使用这个 "),n("code",[t._v("id")]),t._v(" 新建 "),n("code",[t._v("Worker")]),t._v(",并储存进 vector 中")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Worker"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" workers "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Vec"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("with_capacity")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" id "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("size "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("push")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Worker"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("JoinHandle"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" thread "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("spawn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n id"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-15: 修改 "),s("code",[this._v("ThreadPool")]),this._v(" 存放 "),s("code",[this._v("Worker")]),this._v(" 实例而不是直接存放线程")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里将 "),n("code",[t._v("ThreadPool")]),t._v(" 中字段名从 "),n("code",[t._v("threads")]),t._v(" 改为 "),n("code",[t._v("workers")]),t._v(",因为它现在储存 "),n("code",[t._v("Worker")]),t._v(" 而不是 "),n("code",[t._v("JoinHandle<()>")]),t._v("。使用 "),n("code",[t._v("for")]),t._v(" 循环中的计数作为 "),n("code",[t._v("Worker::new")]),t._v(" 的参数,并将每一个新建的 "),n("code",[t._v("Worker")]),t._v(" 储存在叫做 "),n("code",[t._v("workers")]),t._v(" 的 vector 中。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Worker")]),t._v(" 结构体和其 "),n("code",[t._v("new")]),t._v(" 函数是私有的,因为外部代码(比如 "),n("em",[t._v("src/bin/main.rs")]),t._v(" 中的 server)并不需要知道关于 "),n("code",[t._v("ThreadPool")]),t._v(" 中使用 "),n("code",[t._v("Worker")]),t._v(" 结构体的实现细节。"),n("code",[t._v("Worker::new")]),t._v(" 函数使用 "),n("code",[t._v("id")]),t._v(" 参数并储存了使用一个空闭包创建的 "),n("code",[t._v("JoinHandle<()>")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这段代码能够编译并用指定给 "),n("code",[t._v("ThreadPool::new")]),t._v(" 的参数创建储存了一系列的 "),n("code",[t._v("Worker")]),t._v(" 实例,不过 "),n("strong",[t._v("仍然")]),t._v(" 没有处理 "),n("code",[t._v("execute")]),t._v(" 中得到的闭包。让我们聊聊接下来怎么做。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用通道向线程发送请求"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用通道向线程发送请求","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用通道向线程发送请求")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("下一个需要解决的问题是传递给 "),n("code",[t._v("thread::spawn")]),t._v(" 的闭包完全没有做任何工作。目前,我们在 "),n("code",[t._v("execute")]),t._v(" 方法中获得期望执行的闭包,不过在创建 "),n("code",[t._v("ThreadPool")]),t._v(" 的过程中创建每一个 "),n("code",[t._v("Worker")]),t._v(" 时需要向 "),n("code",[t._v("thread::spawn")]),t._v(" 传递一个闭包。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们希望刚创建的 "),s("code",[this._v("Worker")]),this._v(" 结构体能够从 "),s("code",[this._v("ThreadPool")]),this._v(" 的队列中获取需要执行的代码,并发送到线程中执行他们。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在第十六章,我们学习了 "),n("strong",[t._v("通道")]),t._v(" —— 一个沟通两个线程的简单手段 —— 对于这个例子来说则是绝佳的。这里通道将充当任务队列的作用,"),n("code",[t._v("execute")]),t._v(" 将通过 "),n("code",[t._v("ThreadPool")]),t._v(" 向其中线程正在寻找工作的 "),n("code",[t._v("Worker")]),t._v(" 实例发送任务。如下是这个计划:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ol",[n("li",[n("code",[t._v("ThreadPool")]),t._v(" 会创建一个通道并充当发送端。")]),t._v(" "),n("li",[t._v("每个 "),n("code",[t._v("Worker")]),t._v(" 将会充当通道的接收端。")]),t._v(" "),n("li",[t._v("新建一个 "),n("code",[t._v("Job")]),t._v(" 结构体来存放用于向通道中发送的闭包。")]),t._v(" "),n("li",[n("code",[t._v("execute")]),t._v(" 方法会在通道发送端发出期望执行的任务。")]),t._v(" "),n("li",[t._v("在线程中,"),n("code",[t._v("Worker")]),t._v(" 会遍历通道的接收端并执行任何接收到的任务。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们以在 "),s("code",[this._v("ThreadPool::new")]),this._v(" 中创建通道并让 "),s("code",[this._v("ThreadPool")]),this._v(" 实例充当发送端开始,如示例 20-16 所示。"),s("code",[this._v("Job")]),this._v(" 是将在通道中发出的类型;目前它是一个没有任何内容的结构体:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Worker"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Sender"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Job"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Job"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("channel")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" workers "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Vec"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("with_capacity")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" id "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("size "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("push")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Worker"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("JoinHandle"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" thread "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("spawn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# id"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-16: 修改 "),s("code",[this._v("ThreadPool")]),this._v(" 来储存一个发送 "),s("code",[this._v("Job")]),this._v(" 实例的通道发送端")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 "),s("code",[this._v("ThreadPool::new")]),this._v(" 中,新建了一个通道,并接着让线程池在接收端等待。这段代码能够编译,不过仍有警告。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们尝试在线程池创建每个 worker 时将通道的接收端传递给他们。须知我们希望在 worker 所分配的线程中使用通道的接收端,所以将在闭包中引用 "),s("code",[this._v("receiver")]),this._v(" 参数。示例 20-17 中展示的代码还不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("impl ThreadPool {\n // --snip--\n pub fn new(size: usize) -> ThreadPool {\n assert!(size > 0);\n\n let (sender, receiver) = mpsc::channel();\n\n let mut workers = Vec::with_capacity(size);\n\n for id in 0..size {\n workers.push(Worker::new(id, receiver));\n }\n\n ThreadPool {\n workers,\n sender,\n }\n }\n // --snip--\n}\n\n// --snip--\n\nimpl Worker {\n fn new(id: usize, receiver: mpsc::Receiver) -> Worker {\n let thread = thread::spawn(|| {\n receiver;\n });\n\n Worker {\n id,\n thread,\n }\n }\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-17: 将通道的接收端传递给 worker")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这是一些小而直观的修改:将通道的接收端传递进了 "),s("code",[this._v("Worker::new")]),this._v(",并接着在闭包中使用它。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nerror[E0382]: use of moved value: `receiver`\n --\x3e src/lib.rs:27:42\n |\n27 | workers.push(Worker::new(id, receiver));\n | ^^^^^^^^ value moved here in\n previous iteration of loop\n |\n = note: move occurs because `receiver` has type\n `std::sync::mpsc::Receiver`, which does not implement the `Copy` trait\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这段代码尝试将 "),n("code",[t._v("receiver")]),t._v(" 传递给多个 "),n("code",[t._v("Worker")]),t._v(" 实例。这是不行的,回忆第十六章:Rust 所提供的通道实现是多 "),n("strong",[t._v("生产者")]),t._v(",单 "),n("strong",[t._v("消费者")]),t._v(" 的。这意味着不能简单的克隆通道的消费端来解决问题。即便可以,那也不是我们希望使用的技术;我们希望通过在所有的 worker 中共享单一 "),n("code",[t._v("receiver")]),t._v(",在线程间分发任务。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("另外,从通道队列中取出任务涉及到修改 "),s("code",[this._v("receiver")]),this._v(",所以这些线程需要一个能安全的共享和修改 "),s("code",[this._v("receiver")]),this._v(" 的方式,否则可能导致竞争状态(参考第十六章)。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("回忆一下第十六章讨论的线程安全智能指针,为了在多个线程间共享所有权并允许线程修改其值,需要使用 "),s("code",[this._v("Arc>")]),this._v("。"),s("code",[this._v("Arc")]),this._v(" 使得多个 worker 拥有接收端,而 "),s("code",[this._v("Mutex")]),this._v(" 则确保一次只有一个 worker 能从接收端得到任务。示例 20-18 展示了所需的修改:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Arc"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Mutex"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Worker"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Sender"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Job"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Job"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("channel")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" receiver "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Arc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Mutex"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" workers "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Vec"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("with_capacity")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" id "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("size "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("push")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Worker"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Arc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clone")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("JoinHandle"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Arc"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Mutex"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Receiver"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Job"),n("span",{attrs:{class:"token operator"}},[t._v(">>")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" thread "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("spawn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# id"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-18: 使用 "),s("code",[this._v("Arc")]),this._v(" 和 "),s("code",[this._v("Mutex")]),this._v(" 在 worker 间共享通道的接收端")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 "),n("code",[t._v("ThreadPool::new")]),t._v(" 中,将通道的接收端放入一个 "),n("code",[t._v("Arc")]),t._v(" 和一个 "),n("code",[t._v("Mutex")]),t._v(" 中。对于每一个新 worker,克隆 "),n("code",[t._v("Arc")]),t._v(" 来增加引用计数,如此这些 worker 就可以共享接收端的所有权了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"实现-execute-方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#实现-execute-方法","aria-hidden":"true"}},[this._v("#")]),this._v(" 实现 "),s("code",[this._v("execute")]),this._v(" 方法")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("最后让我们实现 "),n("code",[t._v("ThreadPool")]),t._v(" 上的 "),n("code",[t._v("execute")]),t._v(" 方法。同时也要修改 "),n("code",[t._v("Job")]),t._v(" 结构体:它将不再是结构体,"),n("code",[t._v("Job")]),t._v(" 将是一个有着 "),n("code",[t._v("execute")]),t._v(" 接收到的闭包类型的 trait 对象的类型别名。第十九章 “类型别名用来创建类型同义词” 部分提到过,类型别名允许将长的类型变短。观察示例 20-19:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Worker"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Sender"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Job"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Job "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Box"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" execute"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" F"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v("\n F"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" job "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Box"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("send")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("job"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-19: 为存放每一个闭包的 "),s("code",[this._v("Box")]),this._v(" 创建一个 "),s("code",[this._v("Job")]),this._v(" 类型别名,接着在通道中发出任务")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在使用 "),n("code",[t._v("execute")]),t._v(" 得到的闭包新建 "),n("code",[t._v("Job")]),t._v(" 实例之后,将这些任务从通道的发送端发出。这里调用 "),n("code",[t._v("send")]),t._v(" 上的 "),n("code",[t._v("unwrap")]),t._v(",因为发送可能会失败,这可能发生于例如停止了所有线程执行的情况,这意味着接收端停止接收新消息了。不过目前我们无法停止线程执行;只要线程池存在他们就会一直执行。使用 "),n("code",[t._v("unwrap")]),t._v(" 是因为我们知道失败不可能发生,即便编译器不这么认为。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不过到此事情还没有结束!在 worker 中,传递给 "),s("code",[this._v("thread::spawn")]),this._v(" 的闭包仍然还只是 "),s("strong",[this._v("引用")]),this._v(" 了通道的接收端。相反我们需要闭包一直循环,向通道的接收端请求任务,并在得到任务时执行他们。如示例 20-20 对 "),s("code",[this._v("Worker::new")]),this._v(" 做出修改:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('// --snip--\n\nimpl Worker {\n fn new(id: usize, receiver: Arc>>) -> Worker {\n let thread = thread::spawn(move || {\n loop {\n let job = receiver.lock().unwrap().recv().unwrap();\n\n println!("Worker {} got a job; executing.", id);\n\n (*job)();\n }\n });\n\n Worker {\n id,\n thread,\n }\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-20: 在 worker 线程中接收并执行任务")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里,首先在 "),n("code",[t._v("receiver")]),t._v(" 上调用了 "),n("code",[t._v("lock")]),t._v(" 来获取互斥器,接着 "),n("code",[t._v("unwrap")]),t._v(" 在出现任何错误时 panic。如果互斥器处于一种叫做 "),n("strong",[t._v("被污染")]),t._v("("),n("em",[t._v("poisoned")]),t._v(")的状态时获取锁可能会失败,这可能发生于其他线程在持有锁时 panic 了且没有释放锁。在这种情况下,调用 "),n("code",[t._v("unwrap")]),t._v(" 使其 panic 是正确的行为。请随意将 "),n("code",[t._v("unwrap")]),t._v(" 改为包含有意义错误信息的 "),n("code",[t._v("expect")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如果锁定了互斥器,接着调用 "),n("code",[t._v("recv")]),t._v(" 从通道中接收 "),n("code",[t._v("Job")]),t._v("。最后的 "),n("code",[t._v("unwrap")]),t._v(" 也绕过了一些错误,这可能发生于持有通道发送端的线程停止的情况,类似于如果接收端关闭时 "),n("code",[t._v("send")]),t._v(" 方法如何返回 "),n("code",[t._v("Err")]),t._v(" 一样。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("调用 "),n("code",[t._v("recv")]),t._v(" 会 "),n("strong",[t._v("阻塞")]),t._v(" 当前线程,所以如果还没有任务,其会等待直到有可用的任务。"),n("code",[t._v("Mutex")]),t._v(" 确保一次只有一个 "),n("code",[t._v("Worker")]),t._v(" 线程尝试请求任务。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0161]: cannot move a value of type std::ops::FnOnce() +\nstd::marker::Send: the size of std::ops::FnOnce() + std::marker::Send cannot be\nstatically determined\n --\x3e src/lib.rs:63:17\n |\n63 | (*job)();\n | ^^^^^^\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这个错误非常的神秘,因为这个问题本身就很神秘。为了调用储存在 "),n("code",[t._v("Box")]),t._v(" (这正是 "),n("code",[t._v("Job")]),t._v(" 别名的类型)中的 "),n("code",[t._v("FnOnce")]),t._v(" 闭包,该闭包需要能将自己移动 "),n("strong",[t._v("出")]),t._v(" "),n("code",[t._v("Box")]),t._v(",因为当调用这个闭包时,它获取 "),n("code",[t._v("self")]),t._v(" 的所有权。通常来说,将值移动出 "),n("code",[t._v("Box")]),t._v(" 是不被允许的,因为 Rust 不知道 "),n("code",[t._v("Box")]),t._v(" 中的值将会有多大;回忆第十五章能够正常使用 "),n("code",[t._v("Box")]),t._v(" 是因为我们将未知大小的值储存进 "),n("code",[t._v("Box")]),t._v(" 从而得到已知大小的值。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("第十七章曾见过,示例 17-15 中有使用了 "),n("code",[t._v("self: Box")]),t._v(" 语法的方法,它允许方法获取储存在 "),n("code",[t._v("Box")]),t._v(" 中的 "),n("code",[t._v("Self")]),t._v(" 值的所有权。这正是我们希望做的,然而不幸的是 Rust 不允许我们这么做:Rust 当闭包被调用时行为的那部分并没有使用 "),n("code",[t._v("self: Box")]),t._v(" 实现。所以这里 Rust 也不知道它可以使用 "),n("code",[t._v("self: Box")]),t._v(" 来获取闭包的所有权并将闭包移动出 "),n("code",[t._v("Box")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("不过目前让我们通过一个小技巧来绕过这个问题。可以显式的告诉 Rust 在这里我们可以使用 "),n("code",[t._v("self: Box")]),t._v(" 来获取 "),n("code",[t._v("Box")]),t._v(" 中值的所有权,而一旦获取了闭包的所有权就可以调用它了。这涉及到定义一个新 trait,它带有一个在签名中使用 "),n("code",[t._v("self: Box")]),t._v(" 的方法 "),n("code",[t._v("call_box")]),t._v(",为任何实现了 "),n("code",[t._v("FnOnce()")]),t._v(" 的类型定义这个 trait,修改类型别名来使用这个新 trait,并修改 "),n("code",[t._v("Worker")]),t._v(" 使用 "),n("code",[t._v("call_box")]),t._v(" 方法。这些修改如示例 20-21 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('trait FnBox {\n fn call_box(self: Box);\n}\n\nimpl FnBox for F {\n fn call_box(self: Box) {\n (*self)()\n }\n}\n\ntype Job = Box;\n\n// --snip--\n\nimpl Worker {\n fn new(id: usize, receiver: Arc>>) -> Worker {\n let thread = thread::spawn(move || {\n loop {\n let job = receiver.lock().unwrap().recv().unwrap();\n\n println!("Worker {} got a job; executing.", id);\n\n job.call_box();\n }\n });\n\n Worker {\n id,\n thread,\n }\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-21: 新增一个 trait "),s("code",[this._v("FnBox")]),this._v(" 来绕过当前 "),s("code",[this._v("Box")]),this._v(" 的限制")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("首先,新建了一个叫做 "),n("code",[t._v("FnBox")]),t._v(" 的 trait。这个 trait 有一个方法 "),n("code",[t._v("call_box")]),t._v(",它类似于其他 "),n("code",[t._v("Fn*")]),t._v(" trait 中的 "),n("code",[t._v("call")]),t._v(" 方法,除了它获取 "),n("code",[t._v("self: Box")]),t._v(" 以便获取 "),n("code",[t._v("self")]),t._v(" 的所有权并将值从 "),n("code",[t._v("Box")]),t._v(" 中移动出来。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("接下来,为任何实现了 "),n("code",[t._v("FnOnce()")]),t._v(" trait 的类型 "),n("code",[t._v("F")]),t._v(" 实现 "),n("code",[t._v("FnBox")]),t._v(" trait。这实际上意味着任何 "),n("code",[t._v("FnOnce()")]),t._v(" 闭包都可以使用 "),n("code",[t._v("call_box")]),t._v(" 方法。"),n("code",[t._v("call_box")]),t._v(" 的实现使用 "),n("code",[t._v("(*self)()")]),t._v(" 将闭包移动出 "),n("code",[t._v("Box")]),t._v(" 并调用此闭包。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("现在我们需要 "),n("code",[t._v("Job")]),t._v(" 类型别名是任何实现了新 trait "),n("code",[t._v("FnBox")]),t._v(" 的 "),n("code",[t._v("Box")]),t._v("。这允许我们在得到 "),n("code",[t._v("Job")]),t._v(" 值时使用 "),n("code",[t._v("Worker")]),t._v(" 中的 "),n("code",[t._v("call_box")]),t._v("。为任何 "),n("code",[t._v("FnOnce()")]),t._v(" 闭包都实现了 "),n("code",[t._v("FnBox")]),t._v(" trait 意味着无需对实际在通道中发出的值做任何修改。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("最后,对于 "),s("code",[this._v("Worker::new")]),this._v(" 的线程中所运行的闭包,调用 "),s("code",[this._v("call_box")]),this._v(" 而不是直接执行闭包。现在 Rust 就能够理解我们的行为是正确的了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("通过这个技巧,线程池处于可以运行的状态了!执行 "),s("code",[this._v("cargo run")]),this._v(" 并发起一些请求:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling hello v0.1.0 (file:///projects/hello)\nwarning: field is never used: `workers`\n --\x3e src/lib.rs:7:5\n |\n7 | workers: Vec,\n | ^^^^^^^^^^^^^^^^^^^^\n |\n = note: #[warn(dead_code)] on by default\n\nwarning: field is never used: `id`\n --\x3e src/lib.rs:61:5\n |\n61 | id: usize,\n | ^^^^^^^^^\n |\n = note: #[warn(dead_code)] on by default\n\nwarning: field is never used: `thread`\n --\x3e src/lib.rs:62:5\n |\n62 | thread: thread::JoinHandle<()>,\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = note: #[warn(dead_code)] on by default\n\n Finished dev [unoptimized + debuginfo] target(s) in 0.99 secs\n Running `target/debug/hello`\n Worker 0 got a job; executing.\nWorker 2 got a job; executing.\nWorker 1 got a job; executing.\nWorker 3 got a job; executing.\nWorker 0 got a job; executing.\nWorker 2 got a job; executing.\nWorker 1 got a job; executing.\nWorker 3 got a job; executing.\nWorker 0 got a job; executing.\nWorker 2 got a job; executing.\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("成功了!现在我们有了一个可以异步执行连接的线程池!它绝不会创建超过四个线程,所以当 server 收到大量请求时系统也不会负担过重。如果请求 "),s("code",[this._v("/sleep")]),this._v(",server 也能够通过另外一个线程处理其他请求。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在学习了第十八章的 "),s("code",[this._v("while let")]),this._v(" 循环之后,你可能会好奇为何不能如此编写 worker 线程:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('// --snip--\n\nimpl Worker {\n fn new(id: usize, receiver: Arc>>) -> Worker {\n let thread = thread::spawn(move || {\n while let Ok(job) = receiver.lock().unwrap().recv() {\n println!("Worker {} got a job; executing.", id);\n\n job.call_box();\n }\n });\n\n Worker {\n id,\n thread,\n }\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 20-22: 一个使用 "),s("code",[this._v("while let")]),this._v(" 的 "),s("code",[this._v("Worker::new")]),this._v(" 替代实现")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这段代码可以编译和运行,但是并不会产生所期望的线程行为:一个慢请求仍然会导致其他请求等待执行。如此的原因有些微妙:"),n("code",[t._v("Mutex")]),t._v(" 结构体没有公有 "),n("code",[t._v("unlock")]),t._v(" 方法,因为锁的所有权依赖 "),n("code",[t._v("lock")]),t._v(" 方法返回的 "),n("code",[t._v("LockResult>")]),t._v(" 中 "),n("code",[t._v("MutexGuard")]),t._v(" 的生命周期。这允许借用检查器在编译时确保绝不会在没有持有锁的情况下访问由 "),n("code",[t._v("Mutex")]),t._v(" 守护的资源,不过如果没有认真的思考 "),n("code",[t._v("MutexGuard")]),t._v(" 的生命周期的话,也可能会导致比预期更久的持有锁。因为 "),n("code",[t._v("while")]),t._v(" 表达式中的值在整个块一直处于作用域中,"),n("code",[t._v("job.call_box()")]),t._v(" 调用的过程中其仍然持有锁,这意味着其他 worker 不能接收任务。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("相反通过使用 "),n("code",[t._v("loop")]),t._v(" 并在循环块之内而不是之外获取锁和任务,"),n("code",[t._v("lock")]),t._v(" 方法返回的 "),n("code",[t._v("MutexGuard")]),t._v(" 在 "),n("code",[t._v("let job")]),t._v(" 语句结束之后立刻就被丢弃了。这确保了 "),n("code",[t._v("recv")]),t._v(" 调用过程中持有锁,而在 "),n("code",[t._v("job.call_box()")]),t._v(" 调用前锁就被释放了,这就允许并发处理多个请求了。")])}],!1,null,null,null);e.options.__file="ch20-02-multithreaded.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/106.53b25747.js b/src/.vuepress/dist/assets/js/106.53b25747.js
new file mode 100644
index 0000000..362ae88
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/106.53b25747.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[106],{243:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch20-02-slow-requests.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch20-02-slow-requests.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit d06a6a181fd61704cbf7feb55bc61d518c6469f9")])]),t._v(" "),n("p",[t._v("目前 server 会依次处理每一个请求。这对于向我们这样并不期望有非常大量请求的服务来说是可行的,不过随着程序变得更复杂,这样的串行处理并不是最优的。")]),t._v(" "),n("p",[t._v("因为当前的程序顺序处理处理连接,在完成第一个连接的处理之前不会处理第二个连接。如果一个请求花费很长时间来处理,这段时间接收的请求则不得不等待这个长请求结束,即便这些新请求可以很快就处理完。让我们实际尝试一下。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),n("p",[t._v("现在就可以真切的看出我们的 server 有多么的原始;真实的库将会以更简洁的方式处理多请求识别问题。")]),t._v(" "),t._m(7),t._v(" "),n("p",[t._v("这里有多种办法来改变我们的 web server 使其避免所有请求都排在慢请求之后;其一便是实现一个线程池。")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),n("p",[t._v("线程池允许我们并发处理连接:可以在老连接处理完之前就开始处理新连接。这增加了 server 的吞吐量。")]),t._v(" "),n("p",[t._v("如下是我们将要实现的:不再等待每个请求处理完才开始下一个,我们将每个连接的处理发送给不同的线程。这些线程来此程序启动时分配的四个线程的线程池。限制较少的线程数的原因是如果为每个新来的请求都创建一个新线程,则千万级的请求就造成灾难,他们会用尽服务器的资源并导致所有请求的处理都被终止。")]),t._v(" "),t._m(10),t._v(" "),n("p",[t._v("这个设计是多种改善 web server 吞吐量的方法之一。不过本书并不是有关 web server 的,所以这一种方法是我们将要涉及的。其他的方法有 fork/join 模型和单线程异步 I/O 模型。如果你对这个主题感兴趣,则可以阅读更多关于其他解决方案的内容并尝试用 Rust 实现他们;对于一个像 Rust 这样的底层语言,所有这些方法都是可能的。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"慢请求如何影响吞吐率"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#慢请求如何影响吞吐率","aria-hidden":"true"}},[this._v("#")]),this._v(" 慢请求如何影响吞吐率")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"在当前-server-实现中模拟慢请求"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#在当前-server-实现中模拟慢请求","aria-hidden":"true"}},[this._v("#")]),this._v(" 在当前 server 实现中模拟慢请求")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们看看一个花费很长时间处理的请求对当前的 server 实现有何影响。列表 20-10 展示了对另一个请求的响应代码,"),s("code",[this._v("/sleep")]),this._v(",它会使 server 在响应之前休眠五秒。这将模拟一个慢请求以便体现出 server 在串行的处理请求。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("time"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("prelude"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("net"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("File"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("handle_connection")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" TcpStream"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("512")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# stream"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" get "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('b"GET / HTTP/1.1\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" sleep "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('b"GET /sleep HTTP/1.1\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("status_line"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("starts_with")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("get"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 200 OK\\r\\n\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"hello.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" buffer"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("starts_with")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sleep"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("sleep")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from_secs")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 200 OK\\r\\n\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"hello.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"HTTP/1.1 404 NOT FOUND\\r\\n\\r\\n"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"404.html"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 20-10:通过识别 "),s("code",[this._v("/sleep")]),this._v(" 并休眠五秒来模拟慢请求")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这段代码有些凌乱,不过对于模拟的目的来说已经足够!这里创建了第二个请求 "),n("code",[t._v("sleep")]),t._v(",我们会识别其数据。在 "),n("code",[t._v("if")]),t._v(" 块之后增加了一个 "),n("code",[t._v("else if")]),t._v(" 来检查 "),n("code",[t._v("/sleep")]),t._v(" 请求,当发现这个请求时,在渲染欢迎页面之前会先休眠五秒。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("使用 "),n("code",[t._v("cargo run")]),t._v(" 启动 server,并接着打开两个浏览器窗口:一个请求 "),n("code",[t._v("http://localhost:8080/")]),t._v(" 而另一个请求 "),n("code",[t._v("http://localhost:8080/sleep")]),t._v("。如果像之前一样多次请求 "),n("code",[t._v("/")]),t._v(",会发现响应的比较快速。不过如果请求"),n("code",[t._v("/sleep")]),t._v(" 之后在请求 "),n("code",[t._v("/")]),t._v(",就会看到 "),n("code",[t._v("/")]),t._v(" 会等待直到 "),n("code",[t._v("sleep")]),t._v(" 休眠完五秒之后才出现。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用线程池改善吞吐量"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用线程池改善吞吐量","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用线程池改善吞吐量")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("线程池")]),this._v("("),s("em",[this._v("thread pool")]),this._v(")是一组预先分配的用来处理任务的线程。当程序收到一个新任务,线程池中的一个线程会被分配任务并开始处理。其余的线程则可用于处理在第一个线程处理任务的同时处理其他接收到的任务。当第一个线程处理完任务时,它会返回空闲线程池中等待处理新任务。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("不同于分配无限的线程,线程池中将有固定数量的等待线程。当新进请求时,将请求发送到线程池中做处理。线程池会维护一个接收请求的队列。每一个线程会从队列中取出一个请求,处理请求,接着向对队列索取另一个请求。通过这种设计,则可以并发处理 "),n("code",[t._v("N")]),t._v(" 个请求,其中 "),n("code",[t._v("N")]),t._v(" 为线程数。这仍然意味着 "),n("code",[t._v("N")]),t._v(" 个慢请求会阻塞队列中的请求,不过确实将能够处理的慢请求数量从一增加到了 "),n("code",[t._v("N")]),t._v("。")])}],!1,null,null,null);e.options.__file="ch20-02-slow-requests.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/107.a6f25383.js b/src/.vuepress/dist/assets/js/107.a6f25383.js
new file mode 100644
index 0000000..3ea78da
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/107.a6f25383.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[107],{242:function(t,e,s){"use strict";s.r(e);var n=s(0),a=Object(n.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch20-03-designing-the-interface.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch20-03-designing-the-interface.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit d06a6a181fd61704cbf7feb55bc61d518c6469f9")])]),t._v(" "),s("p",[t._v("让我们讨论一下线程池看起来怎样。库作者们经常会发现,当尝试设计一些代码时,首先编写客户端接口确实有助于指导代码设计。以期望的调用方式来构建 API 代码的结构,接着在这个结构之内实现功能,而不是先实现功能再设计公有 API。")]),t._v(" "),s("p",[t._v("类似于第十二章项目中使用的测试驱动开发。这里将要使用编译器驱动开发(Compiler Driven Development)。我们将编写调用所期望的函数的代码,接着依靠编译器告诉我们接下来需要修改什么。编译器错误信息会指导我们的实现。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),s("p",[t._v("再次尝试运行来得到下一个需要解决的错误:")]),t._v(" "),t._m(23),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._m(27),t._v(" "),s("p",[t._v("再次编译检查这段代码:")]),t._v(" "),t._m(28),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._m(38),t._v(" "),t._m(39)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"设计线程池接口"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#设计线程池接口","aria-hidden":"true"}},[this._v("#")]),this._v(" 设计线程池接口")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"如果使用-thread-spawn-的代码结构"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#如果使用-thread-spawn-的代码结构","aria-hidden":"true"}},[this._v("#")]),this._v(" 如果使用 "),e("code",[this._v("thread::spawn")]),this._v(" 的代码结构")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("首先,让我们探索一下为每一个连接都创建一个线程看起来如何。这并不是最终方案,因为正如之前讲到的它会潜在的分配无限的线程,不过这是一个开始。列表 20-11 展示了 "),e("code",[this._v("main")]),this._v(" 的改变,它在 "),e("code",[this._v("for")]),this._v(" 循环中为每一个流分配了一个新线程进行处理:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("Filename: src/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,no_run extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('# use std::thread;\n# use std::io::prelude::*;\n# use std::net::TcpListener;\n# use std::net::TcpStream;\n#\nfn main() {\n let listener = TcpListener::bind("127.0.0.1:8080").unwrap();\n\n for stream in listener.incoming() {\n let stream = stream.unwrap();\n\n thread::spawn(|| {\n handle_connection(stream);\n });\n }\n}\n# fn handle_connection(mut stream: TcpStream) {}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("列表 20-11:为每一个流新建一个线程")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("正如第十六章讲到的,"),s("code",[t._v("thread::spawn")]),t._v(" 会创建一个新线程并运行闭包中的代码。如果运行这段代码并在两个浏览器标签页中加载 "),s("code",[t._v("/sleep")]),t._v(" 和 "),s("code",[t._v("/")]),t._v(",确实会发现 "),s("code",[t._v("/")]),t._v(" 请求并没有等待 "),s("code",[t._v("/sleep")]),t._v(" 结束。不过正如之前提到的,这最终会使系统崩溃因为我们无限制的创建新线程。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"为-threadpool-创建一个类似的接口"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#为-threadpool-创建一个类似的接口","aria-hidden":"true"}},[this._v("#")]),this._v(" 为 "),e("code",[this._v("ThreadPool")]),this._v(" 创建一个类似的接口")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("我们期望线程池以类似且熟悉的方式工作,以便从线程切换到线程池并不会对运行于线程池中的代码做出较大的修改。列表 20-12 展示我们希望用来替换 "),e("code",[this._v("thread::spawn")]),this._v(" 的 "),e("code",[this._v("ThreadPool")]),this._v(" 结构体的假想接口:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,no_run extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('# use std::thread;\n# use std::io::prelude::*;\n# use std::net::TcpListener;\n# use std::net::TcpStream;\n# struct ThreadPool;\n# impl ThreadPool {\n# fn new(size: u32) -> ThreadPool { ThreadPool }\n# fn execute(&self, f: F)\n# where F: FnOnce() + Send + \'static {}\n# }\n#\nfn main() {\n let listener = TcpListener::bind("127.0.0.1:8080").unwrap();\n let pool = ThreadPool::new(4);\n\n for stream in listener.incoming() {\n let stream = stream.unwrap();\n\n pool.execute(|| {\n handle_connection(stream);\n });\n }\n}\n# fn handle_connection(mut stream: TcpStream) {}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("列表 20-12:如何使用我们将要实现的 "),e("code",[this._v("ThreadPool")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("这里使用 "),s("code",[t._v("ThreadPool::new")]),t._v(" 来创建一个新的线程池,它有一个可配置的线程数的参数,在这里是四。这样在 "),s("code",[t._v("for")]),t._v(" 循环中,"),s("code",[t._v("pool.execute")]),t._v(" 将会以类似 "),s("code",[t._v("thread::spawn")]),t._v(" 的方式工作。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"采用编译器驱动开发来驱动-api-的编译"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#采用编译器驱动开发来驱动-api-的编译","aria-hidden":"true"}},[this._v("#")]),this._v(" 采用编译器驱动开发来驱动 API 的编译")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("继续并对列表 20-12 中的 "),e("em",[this._v("src/main.rs")]),this._v(" 做出修改,并利用编译器错误来驱动开发。下面是我们得到的第一个错误:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nerror[E0433]: failed to resolve. Use of undeclared type or module `ThreadPool`\n --\x3e src\\main.rs:10:16\n |\n10 | let pool = ThreadPool::new(4);\n | ^^^^^^^^^^^^^^^ Use of undeclared type or module\n `ThreadPool`\n\nerror: aborting due to previous error\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("好的,我们需要一个 "),e("code",[this._v("ThreadPool")]),this._v("。将 "),e("code",[this._v("hello")]),this._v(" crate 从二进制 crate 转换为库 crate 来存放 "),e("code",[this._v("ThreadPool")]),this._v(" 实现,因为线程池实现与我们的 web server 的特定工作相独立。一旦写完了线程池库,就可以在任何工作中使用这个功能,而不仅仅是处理网络请求。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("创建 "),e("em",[this._v("src/lib.rs")]),this._v(" 文件,它包含了目前可用的最简单的 "),e("code",[this._v("ThreadPool")]),this._v(" 定义:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[this._v("pub")]),this._v(" "),e("span",{attrs:{class:"token keyword"}},[this._v("struct")]),this._v(" ThreadPool"),e("span",{attrs:{class:"token punctuation"}},[this._v(";")]),this._v("\n")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("接着创建一个新目录,"),s("em",[t._v("src/bin")]),t._v(",并将二进制 crate 根文件从 "),s("em",[t._v("src/main.rs")]),t._v(" 移动到 "),s("em",[t._v("src/bin/main.rs")]),t._v("。这使得库 crate 成为 "),s("em",[t._v("hello")]),t._v(" 目录的主要 crate;不过仍然可以使用 "),s("code",[t._v("cargo run")]),t._v(" 运行 "),s("em",[t._v("src/bin/main.rs")]),t._v(" 二进制文件。移动了 "),s("em",[t._v("main.rs")]),t._v(" 文件之后,修改文件开头加入如下代码来引入库 crate 并将 "),s("code",[t._v("ThreadPool")]),t._v(" 引入作用域:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/bin/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("extern crate hello;\nuse hello::ThreadPool;\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nerror: no associated item named `new` found for type `hello::ThreadPool` in the\ncurrent scope\n --\x3e src\\main.rs:13:16\n |\n13 | let pool = ThreadPool::new(4);\n | ^^^^^^^^^^^^^^^\n |\n")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("好的,下一步是为 "),s("code",[t._v("ThreadPool")]),t._v(" 创建一个叫做 "),s("code",[t._v("new")]),t._v(" 的关联函数。我们还知道 "),s("code",[t._v("new")]),t._v(" 需要有一个参数可以接受 "),s("code",[t._v("4")]),t._v(",而且 "),s("code",[t._v("new")]),t._v(" 应该返回 "),s("code",[t._v("ThreadPool")]),t._v(" 实例。让我们实现拥有此特征的最小化 "),s("code",[t._v("new")]),t._v(" 函数:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件夹: src/lib.rs")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool"),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("new")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ThreadPool\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("这里的 "),s("code",[t._v("size")]),t._v(" 参数是 "),s("code",[t._v("u32")]),t._v(" 类型,因为我们知道为负的线程数没有意义。"),s("code",[t._v("u32")]),t._v(" 是一个很好的默认值。一旦真正实现了 "),s("code",[t._v("new")]),t._v(",我们将考虑实现需要选择什么类型,目前我们仅仅处理编译器错误。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nwarning: unused variable: `size`, #[warn(unused_variables)] on by default\n --\x3e src/lib.rs:4:16\n |\n4 | pub fn new(size: u32) -> ThreadPool {\n | ^^^^\n\nerror: no method named `execute` found for type `hello::ThreadPool` in the\ncurrent scope\n --\x3e src/main.rs:18:14\n |\n18 | pool.execute(|| {\n | ^^^^^^^\n")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("好的,一个警告和一个错误。暂时先忽略警告,错误是因为并没有 "),s("code",[t._v("ThreadPool")]),t._v(" 上的 "),s("code",[t._v("execute")]),t._v(" 方法。让我们来定义一个,它应该能接受一个闭包。如果你还记得第十三章,闭包作为参数时可以使用三个不同的 trait:"),s("code",[t._v("Fn")]),t._v("、"),s("code",[t._v("FnMut")]),t._v(" 和 "),s("code",[t._v("FnOnce")]),t._v("。那么应该用哪一种闭包呢?好吧,最终需要实现的类似于 "),s("code",[t._v("thread::spawn")]),t._v(";"),s("code",[t._v("thread::spawn")]),t._v(" 的签名在其参数中使用了何种 bound 呢?查看文档会发现:")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" spawn"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" T"),s("span",{attrs:{class:"token operator"}},[t._v(">")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" F"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" JoinHandle"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),s("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v("\n F"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" T "),s("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),s("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static,")]),t._v("\n T"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Send "),s("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),t._v("\n")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[s("code",[t._v("F")]),t._v(" 是这里我们关心的参数;"),s("code",[t._v("T")]),t._v(" 与返回值有关所以我们并不关心。考虑到 "),s("code",[t._v("spawn")]),t._v(" 使用 "),s("code",[t._v("FnOnce")]),t._v(" 作为 "),s("code",[t._v("F")]),t._v(" 的 trait bound,这可能也是我们需要的,因为最终会将传递给 "),s("code",[t._v("execute")]),t._v(" 的参数传给 "),s("code",[t._v("spawn")]),t._v("。因为处理请求的线程只会执行闭包一次,这也进一步确认了 "),s("code",[t._v("FnOnce")]),t._v(" 是我们需要的 trait。")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[s("code",[t._v("F")]),t._v(" 还有 trait bound "),s("code",[t._v("Send")]),t._v(" 和生命周期绑定 "),s("code",[t._v("'static")]),t._v(",这对我们的情况也是有意义的:需要 "),s("code",[t._v("Send")]),t._v(" 来将闭包从一个线程转移到另一个线程,而 "),s("code",[t._v("'static")]),t._v(" 是因为并不知道线程会执行多久。让我们编写一个使用这些 bound 的泛型参数 "),s("code",[t._v("F")]),t._v(" 的 "),s("code",[t._v("ThreadPool")]),t._v(" 的 "),s("code",[t._v("execute")]),t._v(" 方法:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[t._v("# "),s("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool"),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" execute"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),s("span",{attrs:{class:"token operator"}},[t._v(">")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token operator"}},[t._v("&")]),s("span",{attrs:{class:"token keyword"}},[t._v("self")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" F"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v("\n F"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),s("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("code",[this._v("FnOnce")]),this._v(" trait 仍然需要之后的 "),e("code",[this._v("()")]),this._v(",因为这里的 "),e("code",[this._v("FnOnce")]),this._v(" 代表一个没有参数也没有返回值的闭包。正如函数的定义,返回值类型可以从签名中省略,不过即便没有参数也需要括号。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("因为我们仍在努力使接口能够编译,这里增加了 "),e("code",[this._v("execute")]),this._v(" 方法的最小化实现,它没有做任何工作。再次进行检查:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nwarning: unused variable: `size`, #[warn(unused_variables)] on by default\n --\x3e src/lib.rs:4:16\n |\n4 | pub fn new(size: u32) -> ThreadPool {\n | ^^^^\n\nwarning: unused variable: `f`, #[warn(unused_variables)] on by default\n --\x3e src/lib.rs:8:30\n |\n8 | pub fn execute(&self, f: F)\n | ^\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("现在就只有警告了!能够编译了!注意如果尝试 "),e("code",[this._v("cargo run")]),this._v(" 运行程序并在浏览器中发起请求,仍会在浏览器中出现在本章开始时那样的错误。这个库实际上还没有调用传递给 "),e("code",[this._v("execute")]),this._v(" 的闭包!")])},function(){var t=this.$createElement,e=this._self._c||t;return e("blockquote",[e("p",[this._v("一个你可能听说过的关于像 Haskell 和 Rust 这样有严格编译器的语言的说法是“如果代码能够编译,它就能工作”。这是一个提醒大家的好时机,这只是一个说法和一种有时存在的感觉,实际上并不是完全正确的。我们的项目可以编译,不过它绝对没有做任何工作!如果构建一个真实且功能完整的项目,则需花费大量的时间来开始编写单元测试来检查代码能否编译"),e("strong",[this._v("并且")]),this._v("拥有期望的行为。")])])}],!1,null,null,null);a.options.__file="ch20-03-designing-the-interface.md";e.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/108.524846e6.js b/src/.vuepress/dist/assets/js/108.524846e6.js
new file mode 100644
index 0000000..bc26b84
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/108.524846e6.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[108],{239:function(t,n,s){"use strict";s.r(n);var a=s(0),e=Object(a.a)({},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch20-03-graceful-shutdown-and-cleanup.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch20-03-graceful-shutdown-and-cleanup.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit 1f0136399ba2f5540ecc301fab04bd36492e5554")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),s("p",[t._v("如下是尝试编译代码时得到的错误:")]),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),s("p",[t._v("现在依靠编译器来找出其他需要修改的地方。check 代码会得到两个错误:")]),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),t._m(43),t._v(" "),s("p",[t._v("你不会希望真实世界的 web server 只处理两次请求就停机了,这只是为了展示优雅停机和清理处于正常工作状态。")]),t._v(" "),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._m(47),t._v(" "),s("p",[t._v("这个特定的运行过程中一个有趣的地方在于:注意我们向通道中发出终止消息,而在任何线程收到消息之前,就尝试 join worker 0 了。worker 0 还没有收到终止消息,所以主线程阻塞直到 worker 0 结束。与此同时,每一个线程都收到了终止消息。一旦 worker 0 结束,主线程就等待其他 worker 结束,此时他们都已经收到终止消息并能够停止了。")]),t._v(" "),s("p",[t._v("恭喜!现在我们完成了这个项目,也有了一个使用线程池异步响应请求的基础 web server。我们能对 server 执行优雅停机,它会清理线程池中的所有线程。如下是完整的代码参考:")]),t._v(" "),t._m(48),t._v(" "),t._m(49),t._m(50),t._v(" "),t._m(51),s("p",[t._v("这里还有很多可以做的事!如果你希望继续增强这个项目,如下是一些点子:")]),t._v(" "),t._m(52),t._v(" "),t._m(53),t._v(" "),s("p",[t._v("好极了!你结束了本书的学习!由衷感谢你与我们一道加入这次 Rust 之旅。现在你已经准备好出发并实现自己的 Rust 项目并帮助他人了。请不要忘记我们的社区,这里有其他 Rustaceans 正乐于帮助你迎接 Rust 之路上的任何挑战。")])])},[function(){var t=this.$createElement,n=this._self._c||t;return n("h2",{attrs:{id:"优雅停机与清理"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#优雅停机与清理","aria-hidden":"true"}},[this._v("#")]),this._v(" 优雅停机与清理")])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[t._v("示例 20-21 中的代码如期通过使用线程池异步的响应请求。这里有一些警告说 "),s("code",[t._v("workers")]),t._v("、"),s("code",[t._v("id")]),t._v(" 和 "),s("code",[t._v("thread")]),t._v(" 字段没有直接被使用,这提醒了我们并没有清理所有的内容。当使用不那么优雅的 "),s("span",{staticClass:"keystroke"},[t._v("ctrl-C")]),t._v(" 终止主线程时,所有其他线程也会立刻停止,即便它们正处于处理请求的过程中。")])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[t._v("现在我们要为 "),s("code",[t._v("ThreadPool")]),t._v(" 实现 "),s("code",[t._v("Drop")]),t._v(" trait 对线程池中的每一个线程调用 "),s("code",[t._v("join")]),t._v(",这样这些线程将会执行完他们的请求。接着会为 "),s("code",[t._v("ThreadPool")]),t._v(" 实现一个告诉线程他们应该停止接收新请求并结束的方式。为了实践这些代码,修改 server 在优雅停机(graceful shutdown)之前只接受两个请求。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("h3",{attrs:{id:"为-threadpool-实现-drop-trait"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#为-threadpool-实现-drop-trait","aria-hidden":"true"}},[this._v("#")]),this._v(" 为 "),n("code",[this._v("ThreadPool")]),this._v(" 实现 "),n("code",[this._v("Drop")]),this._v(" Trait")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[this._v("现在开始为线程池实现 "),n("code",[this._v("Drop")]),this._v("。当线程池被丢弃时,应该 join 所有线程以确保他们完成其操作。示例 20-23 展示了 "),n("code",[this._v("Drop")]),this._v(" 实现的第一次尝试;这些代码还不能够编译:")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"language-rust,ignore extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v('impl Drop for ThreadPool {\n fn drop(&mut self) {\n for worker in &mut self.workers {\n println!("Shutting down worker {}", worker.id);\n\n worker.thread.join().unwrap();\n }\n }\n}\n')])])])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"caption"},[this._v("示例 20-23: 当线程池离开作用域时 join 每个线程")])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[t._v("这里首先遍历线程池中的每个 "),s("code",[t._v("workers")]),t._v("。这里使用了 "),s("code",[t._v("&mut")]),t._v(" 因为 "),s("code",[t._v("self")]),t._v(" 本身是一个可变引用而且也需要能够修改 "),s("code",[t._v("worker")]),t._v("。对于每一个线程,会打印出说明信息表明此特定 worker 正在关闭,接着在 worker 线程上调用 "),s("code",[t._v("join")]),t._v("。如果 "),s("code",[t._v("join")]),t._v(" 调用失败,通过 "),s("code",[t._v("unwrap")]),t._v(" 使得 panic 并进行不优雅的关闭。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"language-text extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v("error[E0507]: cannot move out of borrowed content\n --\x3e src/lib.rs:65:13\n |\n65 | worker.thread.join().unwrap();\n | ^^^^^^ cannot move out of borrowed content\n")])])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[t._v("这告诉我们并不能调用 "),s("code",[t._v("join")]),t._v(",因为只有每一个 "),s("code",[t._v("worker")]),t._v(" 的可变借用,而 "),s("code",[t._v("join")]),t._v(" 获取其参数的所有权。为了解决这个问题,需要一个方法将 "),s("code",[t._v("thread")]),t._v(" 移动出拥有其所有权的 "),s("code",[t._v("Worker")]),t._v(" 实例以便 "),s("code",[t._v("join")]),t._v(" 可以消费这个线程。示例 17-15 中我们曾见过这么做的方法:如果 "),s("code",[t._v("Worker")]),t._v(" 存放的是 "),s("code",[t._v("Option")]),t._v(",就可以在 "),s("code",[t._v("Option")]),t._v(" 上调用 "),s("code",[t._v("take")]),t._v(" 方法将值从 "),s("code",[t._v("Some")]),t._v(" 成员中移动出来而对 "),s("code",[t._v("None")]),t._v(" 成员不做处理。换句话说,正在运行的 "),s("code",[t._v("Worker")]),t._v(" 的 "),s("code",[t._v("thread")]),t._v(" 将是 "),s("code",[t._v("Some")]),t._v(" 成员值,而当需要清理 worker 时,将 "),s("code",[t._v("Some")]),t._v(" 替换为 "),s("code",[t._v("None")]),t._v(",这样 worker 就没有可以运行的线程了。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[this._v("为此需要更新 "),n("code",[this._v("Worker")]),this._v(" 的定义为如下:")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[t._v("# "),s("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Worker "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n id"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Option"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("JoinHandle"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token operator"}},[t._v(">>")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"language-text extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v("error[E0599]: no method named `join` found for type\n`std::option::Option>` in the current scope\n --\x3e src/lib.rs:65:27\n |\n65 | worker.thread.join().unwrap();\n | ^^^^\n\nerror[E0308]: mismatched types\n --\x3e src/lib.rs:89:13\n |\n89 | thread,\n | ^^^^^^\n | |\n | expected enum `std::option::Option`, found struct\n `std::thread::JoinHandle`\n | help: try using a variant of the expected type: `Some(thread)`\n |\n = note: expected type `std::option::Option>`\n found type `std::thread::JoinHandle<_>`\n")])])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[t._v("让我们修复第二个错误,它指向 "),s("code",[t._v("Worker::new")]),t._v(" 结尾的代码;当新建 "),s("code",[t._v("Worker")]),t._v(" 时需要将 "),s("code",[t._v("thread")]),t._v(" 值封装进 "),s("code",[t._v("Some")]),t._v("。做出如下改变以修复问题:")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"language-rust,ignore extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v("impl Worker {\n fn new(id: usize, receiver: Arc>>) -> Worker {\n // --snip--\n\n Worker {\n id,\n thread: Some(thread),\n }\n }\n}\n")])])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[t._v("第一个错误位于 "),s("code",[t._v("Drop")]),t._v(" 实现中。之前提到过要调用 "),s("code",[t._v("Option")]),t._v(" 上的 "),s("code",[t._v("take")]),t._v(" 将 "),s("code",[t._v("thread")]),t._v(" 移动出 "),s("code",[t._v("worker")]),t._v("。如下改变会修复问题:")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"language-rust,ignore extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v('impl Drop for ThreadPool {\n fn drop(&mut self) {\n for worker in &mut self.workers {\n println!("Shutting down worker {}", worker.id);\n\n if let Some(thread) = worker.thread.take() {\n thread.join().unwrap();\n }\n }\n }\n}\n')])])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[t._v("如第十七章我们见过的,"),s("code",[t._v("Option")]),t._v(" 上的 "),s("code",[t._v("take")]),t._v(" 方法会取出 "),s("code",[t._v("Some")]),t._v(" 而留下 "),s("code",[t._v("None")]),t._v("。使用 "),s("code",[t._v("if let")]),t._v(" 解构 "),s("code",[t._v("Some")]),t._v(" 并得到线程,接着在线程上调用 "),s("code",[t._v("join")]),t._v("。如果 worker 的线程已然是 "),s("code",[t._v("None")]),t._v(",就知道此时这个 worker 已经清理了其线程所以无需做任何操作。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("h3",{attrs:{id:"向线程发送信号使其停止接收任务"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#向线程发送信号使其停止接收任务","aria-hidden":"true"}},[this._v("#")]),this._v(" 向线程发送信号使其停止接收任务")])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[t._v("有了这些修改,代码就能编译且没有任何警告。不过也有坏消息,这些代码还不能以我们期望的方式运行。问题的关键在于 "),s("code",[t._v("Worker")]),t._v(" 中分配的线程所运行的闭包中的逻辑:调用 "),s("code",[t._v("join")]),t._v(" 并不会关闭线程,因为他们一直 "),s("code",[t._v("loop")]),t._v(" 来寻找任务。如果采用这个实现来尝试丢弃 "),s("code",[t._v("ThreadPool")]),t._v(" ,则主线程会永远阻塞在等待第一个线程结束上。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[this._v("为了修复这个问题,修改线程既监听是否有 "),n("code",[this._v("Job")]),this._v(" 运行也要监听一个应该停止监听并退出无限循环的信号。所以通道将发送这个枚举的两个成员之一而不是 "),n("code",[this._v("Job")]),this._v(" 实例:")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[t._v("# "),s("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Job"),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Message "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token function"}},[t._v("NewJob")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Job"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Terminate"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[s("code",[t._v("Message")]),t._v(" 枚举要么是存放了线程需要运行的 "),s("code",[t._v("Job")]),t._v(" 的 "),s("code",[t._v("NewJob")]),t._v(" 成员,要么是会导致线程退出循环并终止的 "),s("code",[t._v("Terminate")]),t._v(" 成员。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[this._v("同时需要修改通道来使用 "),n("code",[this._v("Message")]),this._v(" 类型值而不是 "),n("code",[this._v("Job")]),this._v(",如示例 20-24 所示:")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"language-rust,ignore extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v('pub struct ThreadPool {\n workers: Vec,\n sender: mpsc::Sender,\n}\n\n// --snip--\n\nimpl ThreadPool {\n // --snip--\n\n pub fn execute(&self, f: F)\n where\n F: FnOnce() + Send + \'static\n {\n let job = Box::new(f);\n\n self.sender.send(Message::NewJob(job)).unwrap();\n }\n}\n\n// --snip--\n\nimpl Worker {\n fn new(id: usize, receiver: Arc>>) ->\n Worker {\n\n let thread = thread::spawn(move ||{\n loop {\n let message = receiver.lock().unwrap().recv().unwrap();\n\n match message {\n Message::NewJob(job) => {\n println!("Worker {} got a job; executing.", id);\n\n job.call_box();\n },\n Message::Terminate => {\n println!("Worker {} was told to terminate.", id);\n\n break;\n },\n }\n }\n });\n\n Worker {\n id,\n thread: Some(thread),\n }\n }\n}\n')])])])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"caption"},[this._v("示例 20-24: 收发 "),n("code",[this._v("Message")]),this._v(" 值并在 "),n("code",[this._v("Worker")]),this._v(" 收到 "),n("code",[this._v("Message::Terminate")]),this._v(" 时退出循环")])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[t._v("为了适用 "),s("code",[t._v("Message")]),t._v(" 枚举需要将两个地方的 "),s("code",[t._v("Job")]),t._v(" 修改为 "),s("code",[t._v("Message")]),t._v(":"),s("code",[t._v("ThreadPool")]),t._v(" 的定义和 "),s("code",[t._v("Worker::new")]),t._v(" 的签名。"),s("code",[t._v("ThreadPool")]),t._v(" 的 "),s("code",[t._v("execute")]),t._v(" 方法需要发送封装进 "),s("code",[t._v("Message::NewJob")]),t._v(" 成员的任务。然后,在 "),s("code",[t._v("Worker::new")]),t._v(" 中当从通道接收 "),s("code",[t._v("Message")]),t._v(" 时,当获取到 "),s("code",[t._v("NewJob")]),t._v("成员会处理任务而收到 "),s("code",[t._v("Terminate")]),t._v(" 成员则会退出循环。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[this._v("通过这些修改,代码再次能够编译并继续按照期望的行为运行。不过还是会得到一个警告,因为并没有创建任何 "),n("code",[this._v("Terminate")]),this._v(" 成员的消息。如示例 20-25 所示修改 "),n("code",[this._v("Drop")]),this._v(" 实现来修复此问题:")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"language-rust,ignore extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v('impl Drop for ThreadPool {\n fn drop(&mut self) {\n println!("Sending terminate message to all workers.");\n\n for _ in &mut self.workers {\n self.sender.send(Message::Terminate).unwrap();\n }\n\n println!("Shutting down all workers.");\n\n for worker in &mut self.workers {\n println!("Shutting down worker {}", worker.id);\n\n if let Some(thread) = worker.thread.take() {\n thread.join().unwrap();\n }\n }\n }\n}\n')])])])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"caption"},[this._v("示例 20-25:在对每个 worker 线程调用 "),n("code",[this._v("join")]),this._v(" 之前向 worker 发送 "),n("code",[this._v("Message::Terminate")])])])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[this._v("现在遍历了 worker 两次,一次向每个 worker 发送一个 "),n("code",[this._v("Terminate")]),this._v(" 消息,一个调用每个 worker 线程上的 "),n("code",[this._v("join")]),this._v("。如果尝试在同一循环中发送消息并立即 join 线程,则无法保证当前迭代的 worker 是从通道收到终止消息的 worker。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[this._v("为了更好的理解为什么需要两个分开的循环,想象一下只有两个 worker 的场景。如果在一个单独的循环中遍历每个 worker,在第一次迭代中向通道发出终止消息并对第一个 worker 线程调用 "),n("code",[this._v("join")]),this._v("。我们会一直等待第一个 worker 结束,不过它永远也不会结束因为第二个线程接收了终止消息。死锁!")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[this._v("为了避免此情况,首先在一个循环中向通道发出所有的 "),n("code",[this._v("Terminate")]),this._v(" 消息,接着在另一个循环中 join 所有的线程。每个 worker 一旦收到终止消息即会停止从通道接收消息,意味着可以确保如果发送同 worker 数相同的终止消息,在 join 之前每个线程都会收到一个终止消息。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[this._v("为了实践这些代码,如示例 20-26 所示修改 "),n("code",[this._v("main")]),this._v(" 在优雅停机 server 之前只接受两个请求:")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"filename"},[this._v("文件名: src/bin/main.rs")])])},function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"language-rust,ignore extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v('fn main() {\n let listener = TcpListener::bind("127.0.0.1:7878").unwrap();\n let pool = ThreadPool::new(4);\n\n for stream in listener.incoming().take(2) {\n let stream = stream.unwrap();\n\n pool.execute(|| {\n handle_connection(stream);\n });\n }\n\n println!("Shutting down.");\n}\n')])])])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"caption"},[this._v("示例 20-26: 在处理两个请求之后通过退出循环来停止 server")])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[s("code",[t._v("take")]),t._v(" 方法定义于 "),s("code",[t._v("Iterator")]),t._v(" trait,这里限制循环最多头 2 次。"),s("code",[t._v("ThreadPool")]),t._v(" 会在 "),s("code",[t._v("main")]),t._v(" 的结尾离开作用域,而且还会看到 "),s("code",[t._v("drop")]),t._v(" 实现的运行。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[this._v("使用 "),n("code",[this._v("cargo run")]),this._v(" 启动 server,并发起三个请求。第三个请求应该会失败,而终端的输出应该看起来像这样:")])},function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"language-text extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v("$ cargo run\n Compiling hello v0.1.0 (file:///projects/hello)\n Finished dev [unoptimized + debuginfo] target(s) in 1.0 secs\n Running `target/debug/hello`\nWorker 0 got a job; executing.\nWorker 3 got a job; executing.\nShutting down.\nSending terminate message to all workers.\nShutting down all workers.\nShutting down worker 0\nWorker 1 was told to terminate.\nWorker 2 was told to terminate.\nWorker 0 was told to terminate.\nWorker 3 was told to terminate.\nShutting down worker 1\nShutting down worker 2\nShutting down worker 3\n")])])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("p",[t._v("可能会出现不同顺序的 worker 和信息输出。可以从信息中看到服务是如何运行的: worker 0 和 worker 3 获取了头两个请求,接着在第三个请求时,我们停止接收连接。当 "),s("code",[t._v("ThreadPool")]),t._v(" 在 "),s("code",[t._v("main")]),t._v(" 的结尾离开作用域时,其 "),s("code",[t._v("Drop")]),t._v(" 实现开始工作,线程池通知所有线程终止。每个 worker 在收到终止消息时会打印出一个信息,接着线程池调用 "),s("code",[t._v("join")]),t._v(" 来终止每一个 worker 线程。")])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"filename"},[this._v("文件名: src/bin/main.rs")])])},function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"language-rust,ignore extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v('extern crate hello;\nuse hello::ThreadPool;\n\nuse std::io::prelude::*;\nuse std::net::TcpListener;\nuse std::net::TcpStream;\nuse std::fs::File;\nuse std::thread;\nuse std::time::Duration;\n\nfn main() {\n let listener = TcpListener::bind("127.0.0.1:7878").unwrap();\n let pool = ThreadPool::new(4);\n\n for stream in listener.incoming().take(2) {\n let stream = stream.unwrap();\n\n pool.execute(|| {\n handle_connection(stream);\n });\n }\n\n println!("Shutting down.");\n}\n\nfn handle_connection(mut stream: TcpStream) {\n let mut buffer = [0; 512];\n stream.read(&mut buffer).unwrap();\n\n let get = b"GET / HTTP/1.1\\r\\n";\n let sleep = b"GET /sleep HTTP/1.1\\r\\n";\n\n let (status_line, filename) = if buffer.starts_with(get) {\n ("HTTP/1.1 200 OK\\r\\n\\r\\n", "hello.html")\n } else if buffer.starts_with(sleep) {\n thread::sleep(Duration::from_secs(5));\n ("HTTP/1.1 200 OK\\r\\n\\r\\n", "hello.html")\n } else {\n ("HTTP/1.1 404 NOT FOUND\\r\\n\\r\\n", "404.html")\n };\n\n let mut file = File::open(filename).unwrap();\n let mut contents = String::new();\n\n file.read_to_string(&mut contents).unwrap();\n\n let response = format!("{}{}", status_line, contents);\n\n stream.write(response.as_bytes()).unwrap();\n stream.flush().unwrap();\n}\n')])])])},function(){var t=this.$createElement,n=this._self._c||t;return n("p",[n("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("mpsc"),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Arc"),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Mutex"),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Message "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token function"}},[t._v("NewJob")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Job"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Terminate"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Worker"),s("span",{attrs:{class:"token operator"}},[t._v(">")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sender"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mpsc"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Sender"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Message"),s("span",{attrs:{class:"token operator"}},[t._v(">")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" FnBox "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("call_box")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token keyword"}},[t._v("self")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Box"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Self"),s("span",{attrs:{class:"token operator"}},[t._v(">")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("impl")]),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" FnBox "),s("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" F "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("call_box")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token keyword"}},[t._v("self")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Box"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),s("span",{attrs:{class:"token operator"}},[t._v(">")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token operator"}},[t._v("*")]),s("span",{attrs:{class:"token keyword"}},[t._v("self")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Job "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Box"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("FnBox "),s("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),s("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),s("span",{attrs:{class:"token operator"}},[t._v(">")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token comment"}},[t._v("/// Create a new ThreadPool.")]),t._v("\n "),s("span",{attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),s("span",{attrs:{class:"token comment"}},[t._v("/// The size is the number of threads in the pool.")]),t._v("\n "),s("span",{attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),s("span",{attrs:{class:"token comment"}},[t._v("/// # Panics")]),t._v("\n "),s("span",{attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),s("span",{attrs:{class:"token comment"}},[t._v("/// The `new` function will panic if the size is zero.")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("new")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token function"}},[t._v("assert!")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size "),s("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("0")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sender"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" mpsc"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("channel")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" receiver "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Arc"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("new")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Mutex"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("new")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("receiver"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" workers "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Vec"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("with_capacity")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" id "),s("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{attrs:{class:"token number"}},[t._v("0")]),s("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("size "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("push")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Worker"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("new")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Arc"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("clone")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("receiver"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n ThreadPool "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sender"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" execute"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),s("span",{attrs:{class:"token operator"}},[t._v(">")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token operator"}},[t._v("&")]),s("span",{attrs:{class:"token keyword"}},[t._v("self")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" F"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v("\n F"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),s("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" job "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Box"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("new")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("self")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sender"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("send")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Message"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("NewJob")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("job"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("unwrap")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Drop "),s("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" ThreadPool "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("drop")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token operator"}},[t._v("&")]),s("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("self")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token function"}},[t._v("println!")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v('"Sending terminate message to all workers."')]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" _ "),s("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{attrs:{class:"token operator"}},[t._v("&")]),s("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("self")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("workers "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("self")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sender"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("send")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Message"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Terminate"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("unwrap")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{attrs:{class:"token function"}},[t._v("println!")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v('"Shutting down all workers."')]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" worker "),s("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),s("span",{attrs:{class:"token operator"}},[t._v("&")]),s("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("self")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("workers "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token function"}},[t._v("println!")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v('"Shutting down worker {}"')]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" worker"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("id"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("Some")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" worker"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("take")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("join")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("unwrap")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Worker "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n id"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Option"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("JoinHandle"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token operator"}},[t._v(">>")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Worker "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("new")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Arc"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Mutex"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("mpsc"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Receiver"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Message"),s("span",{attrs:{class:"token operator"}},[t._v(">>")]),s("span",{attrs:{class:"token operator"}},[t._v(">")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v("\n Worker "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" thread "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("spawn")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token keyword"}},[t._v("move")]),t._v(" "),s("span",{attrs:{class:"token closure-params"}},[s("span",{attrs:{class:"token punctuation"}},[t._v("|")]),s("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("loop")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" message "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" receiver"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("lock")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("unwrap")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("recv")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("unwrap")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" message "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Message"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("NewJob")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("job"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token function"}},[t._v("println!")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v('"Worker {} got a job; executing."')]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" id"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n job"),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("call_box")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Message"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Terminate "),s("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token function"}},[t._v("println!")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v('"Worker {} was told to terminate."')]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" id"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{attrs:{class:"token keyword"}},[t._v("break")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n Worker "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n id"),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("Some")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("thread"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("ul",[s("li",[t._v("为 "),s("code",[t._v("ThreadPool")]),t._v(" 和其公有方法增加更多文档")]),t._v(" "),s("li",[t._v("为库的功能增加测试")]),t._v(" "),s("li",[t._v("将 "),s("code",[t._v("unwrap")]),t._v(" 调用改为更健壮的错误处理")]),t._v(" "),s("li",[t._v("使用 "),s("code",[t._v("ThreadPool")]),t._v(" 进行其他不同于处理网络请求的任务")]),t._v(" "),s("li",[t._v("在 crates.io 寻找一个线程池 crate 并使用它实现一个类似的 web server,将其 API 和鲁棒性与我们的实现做对比")])])},function(){var t=this.$createElement,n=this._self._c||t;return n("h2",{attrs:{id:"总结"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])}],!1,null,null,null);e.options.__file="ch20-03-graceful-shutdown-and-cleanup.md";n.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/109.5b3e8cc8.js b/src/.vuepress/dist/assets/js/109.5b3e8cc8.js
new file mode 100644
index 0000000..dd4d20f
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/109.5b3e8cc8.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[109],{238:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch20-04-storing-threads.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch20-04-storing-threads.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit d06a6a181fd61704cbf7feb55bc61d518c6469f9")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),n("p",[t._v("如果代码能够工作就应是如此效果,不过他们还不能工作!如果检查他们,会得到一个错误:")]),t._v(" "),t._m(21),t._m(22),t._v(" "),t._m(23),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),n("p",[t._v("让我们做出如下修改:")]),t._v(" "),t._m(27),t._v(" "),n("p",[t._v("如果你渴望挑战,在查看列表 20-15 中的代码之前尝试自己实现这些修改。")]),t._v(" "),n("p",[t._v("准备好了吗?列表 20-15 就是一个做出了这些修改的例子:")]),t._v(" "),t._m(28),t._v(" "),t._m(29),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"创建线程池并储存线程"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#创建线程池并储存线程","aria-hidden":"true"}},[this._v("#")]),this._v(" 创建线程池并储存线程")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("之前的警告是因为在 "),s("code",[this._v("new")]),this._v(" 和 "),s("code",[this._v("execute")]),this._v(" 中没有对参数做任何操作。让我们用期望的实际行为实现他们。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"验证池中的线程数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#验证池中的线程数","aria-hidden":"true"}},[this._v("#")]),this._v(" 验证池中的线程数")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("以考虑 "),n("code",[t._v("new")]),t._v(" 作为开始。之前提到使用无符号类型作为 "),n("code",[t._v("size")]),t._v(" 参数的类型,因为为负的线程数没有意义。然而,零个线程同样没有意义,不过零是一个完全有效的 "),n("code",[t._v("u32")]),t._v(" 值。让我们在返回 "),n("code",[t._v("ThreadPool")]),t._v(" 之前检查 "),n("code",[t._v("size")]),t._v(" 是否大于零,并使用 "),n("code",[t._v("assert!")]),t._v(" 宏在得到零时 panic,如列表 20-13 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("/// Create a new ThreadPool.")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("/// The size is the number of threads in the pool.")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("/// # Panics")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("///")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("/// The `new` function will panic if the size is zero.")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n ThreadPool\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 20-13:实现 "),s("code",[this._v("ThreadPool::new")]),this._v(" 在 "),s("code",[this._v("size")]),this._v(" 为零时 panic")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("趁着这个机会我们用文档注释为 "),n("code",[t._v("ThreadPool")]),t._v(" 增加了一些文档。注意这里遵循了良好的文档实践并增加了一个部分提示函数会 panic 的情况,正如第十四章所讨论的。尝试运行 "),n("code",[t._v("cargo doc --open")]),t._v(" 并点击 "),n("code",[t._v("ThreadPool")]),t._v(" 结构体来查看生成的 "),n("code",[t._v("new")]),t._v(" 的文档看起来如何!")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("相比像这里使用 "),n("code",[t._v("assert!")]),t._v(" 宏,也可以让 "),n("code",[t._v("new")]),t._v(" 像之前 I/O 项目中列表 12-9 中 "),n("code",[t._v("Config::new")]),t._v(" 那样返回一个 "),n("code",[t._v("Result")]),t._v(",不过在这里我们选择创建一个没有任何线程的线程池应该是要给不可恢复的错误。如果你想做的更好,尝试编写一个采用如下签名的 "),n("code",[t._v("new")]),t._v(" 版本来感受一下两者的区别:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("ThreadPool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" PoolCreationError"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"在线程池中储存线程"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#在线程池中储存线程","aria-hidden":"true"}},[this._v("#")]),this._v(" 在线程池中储存线程")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在有了一个有效的线程池线程数,就可以实际创建这些线程并在返回之前将他们储存在 "),s("code",[this._v("ThreadPool")]),this._v(" 结构体中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这引出了另一个问题:如何“储存”一个线程?让我们再看看 "),s("code",[this._v("thread::spawn")]),this._v(" 的签名:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" spawn"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" F"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" JoinHandle"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v("\n F"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" T "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static,")]),t._v("\n T"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Send "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("spawn")]),t._v(" 返回 "),n("code",[t._v("JoinHandle")]),t._v(",其中 "),n("code",[t._v("T")]),t._v(" 是闭包返回的类型。尝试使用 "),n("code",[t._v("JoinHandle")]),t._v(" 来看看会发生什么。在我们的情况中,传递给线程池的闭包会处理连接并不返回任何值,所以 "),n("code",[t._v("T")]),t._v(" 将会是单元类型 "),n("code",[t._v("()")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这还不能编译,不过考虑一下列表 20-14 所示的代码。我们改变了 "),n("code",[t._v("ThreadPool")]),t._v(" 的定义来存放一个 "),n("code",[t._v("thread::JoinHandle<()>")]),t._v(" 的 vector 实例,使用 "),n("code",[t._v("size")]),t._v(" 容量来初始化,并设置一个 "),n("code",[t._v("for")]),t._v(" 循环了来运行创建线程的代码,并返回包含这些线程的 "),n("code",[t._v("ThreadPool")]),t._v(" 实例:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("use std::thread;\n\npub struct ThreadPool {\n threads: Vec>,\n}\n\nimpl ThreadPool {\n // ...snip...\n pub fn new(size: u32) -> ThreadPool {\n assert!(size > 0);\n\n let mut threads = Vec::with_capacity(size);\n\n for _ in 0..size {\n // create some threads and store them in the vector\n }\n\n ThreadPool {\n threads\n }\n }\n\n // ...snip...\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 20-14:为 "),s("code",[this._v("ThreadPool")]),this._v(" 创建一个 vector 来存放线程")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里将 "),s("code",[this._v("std::thread")]),this._v(" 引入库 crate 的作用域,因为使用了 "),s("code",[this._v("thread::JoinHandle")]),this._v(" 作为 "),s("code",[this._v("ThreadPool")]),this._v(" 中 vector 元素的类型。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在得到了有效的数量之后,就可以新建一个存放 "),n("code",[t._v("size")]),t._v(" 个元素的 vector。本书还未使用过 "),n("code",[t._v("with_capacity")]),t._v(";它与 "),n("code",[t._v("Vec::new")]),t._v(" 做了同样的工作,不过有一个重要的区别:它为 vector 预先分配空间。因为已经知道了 vector 中需要 "),n("code",[t._v("size")]),t._v(" 个元素,预先进行分配比仅仅 "),n("code",[t._v("Vec::new")]),t._v(" 要稍微有效率一些,因为 "),n("code",[t._v("Vec::new")]),t._v(" 随着插入元素而重新改变大小。因为一开始就用所需的确定大小来创建 vector,为其增减元素时不会改变底层 vector 的大小。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nerror[E0308]: mismatched types\n --\x3e src\\main.rs:70:46\n |\n70 | let mut threads = Vec::with_capacity(size);\n | ^^^^ expected usize, found u32\n\nerror: aborting due to previous error\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("size")]),t._v(" 是 "),n("code",[t._v("u32")]),t._v(",不过 "),n("code",[t._v("Vec::with_capacity")]),t._v(" 需要一个 "),n("code",[t._v("usize")]),t._v("。这里有两个选择:可以改变函数签名,或者可以将 "),n("code",[t._v("u32")]),t._v(" 转换为 "),n("code",[t._v("usize")]),t._v("。如果你还记得定义 "),n("code",[t._v("new")]),t._v(" 时,并没有仔细考虑有意义的数值类型,只是随便选了一个。现在来进行一些思考吧。考虑到 "),n("code",[t._v("size")]),t._v(" 是 vector 的长度,"),n("code",[t._v("usize")]),t._v(" 就很有道理了。甚至他们的名字都很类似!改变 "),n("code",[t._v("new")]),t._v(" 的签名,这会使列表 20-14 的代码能够编译:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果再次运行 "),s("code",[this._v("cargo check")]),this._v(",会得到一些警告,不过应该能成功编译。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("列表 20-14 的 "),s("code",[this._v("for")]),this._v(" 循环中留下了一个关于创建线程的注释。如何实际创建线程呢?这是一个难题。这些线程应该做什么呢?这里并不知道他们需要做什么,因为 "),s("code",[this._v("execute")]),this._v(" 方法获取闭包并传递给了线程池。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("让我们稍微重构一下:不再储存一个 "),n("code",[t._v("JoinHandle<()>")]),t._v(" 实例的 vector,将创建一下新的结构体来代表 "),n("em",[t._v("worker")]),t._v(" 的概念。worker 会接收 "),n("code",[t._v("execute")]),t._v(" 方法,并会处理实际的闭包调用。另外储存固定 "),n("code",[t._v("size")]),t._v(" 数量的还不知道将要执行什么闭包的 "),n("code",[t._v("Worker")]),t._v(" 实例,也可以为每一个 worker 设置一个 "),n("code",[t._v("id")]),t._v(",这样就可以在日志和调试中区别线程池中的不同 worker。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ol",[n("li",[t._v("定义 "),n("code",[t._v("Worker")]),t._v(" 结构体存放 "),n("code",[t._v("id")]),t._v(" 和 "),n("code",[t._v("JoinHandle<()>")])]),t._v(" "),n("li",[t._v("修改 "),n("code",[t._v("ThreadPool")]),t._v(" 存放一个 "),n("code",[t._v("Worker")]),t._v(" 实例的 vector")]),t._v(" "),n("li",[t._v("定义 "),n("code",[t._v("Worker::new")]),t._v(" 函数,它获取一个 "),n("code",[t._v("id")]),t._v(" 数字并返回一个带有 "),n("code",[t._v("id")]),t._v(" 和用空闭包分配的线程的 "),n("code",[t._v("Worker")]),t._v(" 实例,之后会修复这些")]),t._v(" "),n("li",[t._v("在 "),n("code",[t._v("ThreadPool::new")]),t._v(" 中,使用 "),n("code",[t._v("for")]),t._v(" 循环来计数生成 "),n("code",[t._v("id")]),t._v(",使用这个 "),n("code",[t._v("id")]),t._v(" 新建 "),n("code",[t._v("Worker")]),t._v(",并储存进 vector 中")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Worker"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" workers "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Vec"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("with_capacity")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" id "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("size "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("push")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Worker"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("JoinHandle"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" thread "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("spawn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n id"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 20-15:修改 "),s("code",[this._v("ThreadPool")]),this._v(" 存放 "),s("code",[this._v("Worker")]),this._v(" 实例而不是直接存放线程")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里选择将 "),n("code",[t._v("ThreadPool")]),t._v(" 中字段名从 "),n("code",[t._v("threads")]),t._v(" 改为 "),n("code",[t._v("workers")]),t._v(",因为我们改变了存放内容为 "),n("code",[t._v("Worker")]),t._v(" 而不是 "),n("code",[t._v("JoinHandle<()>")]),t._v("。使用 "),n("code",[t._v("for")]),t._v(" 循环中的计数作为 "),n("code",[t._v("Worker::new")]),t._v(" 的参数,并将每一个新建的 "),n("code",[t._v("Worker")]),t._v(" 储存在叫做 "),n("code",[t._v("workers")]),t._v(" 的 vector 中。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Worker")]),t._v(" 结构体和其 "),n("code",[t._v("new")]),t._v(" 函数是私有的,因为外部代码(比如 "),n("em",[t._v("src/bin/main.rs")]),t._v(" 中的 server)并不需要 "),n("code",[t._v("ThreadPool")]),t._v(" 中使用 "),n("code",[t._v("Worker")]),t._v(" 结构体的实现细节。"),n("code",[t._v("Worker::new")]),t._v(" 函数使用 "),n("code",[t._v("id")]),t._v(" 参数并储存了使用一个空闭包创建的 "),n("code",[t._v("JoinHandle<()>")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这段代码能够编译并用指定给 "),n("code",[t._v("ThreadPool::new")]),t._v(" 的参数创建储存了一系列的 "),n("code",[t._v("Worker")]),t._v(" 实例,不过"),n("strong",[t._v("仍然")]),t._v("没有处理 "),n("code",[t._v("execute")]),t._v(" 中得到的闭包。让我们聊聊接下来怎么做。")])}],!1,null,null,null);e.options.__file="ch20-04-storing-threads.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/11.7f9bf643.js b/src/.vuepress/dist/assets/js/11.7f9bf643.js
new file mode 100644
index 0000000..b5e269d
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/11.7f9bf643.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{272:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/appendix-06-newest-features.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("appendix-06-newest-features.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit b64de01431cdf1020ad3358d2f83e46af68a39ed")])]),t._v(" "),a("p",[t._v("自从本书的主要部分完成之后,该附录文档中的特性已经加到 Rust 的稳定版中。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),a("p",[t._v("如果有一个包含许多不同子模块的复杂模块树,然后需要从每个子模块中引入几个特性,那么将所有导入模块放在同一声明中来保持代码清洁同时避免根模块名重复将会非常有用。")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),a("p",[t._v("Rust 1.26.0 添加了 128 字节的整数基本类型:")]),t._v(" "),t._m(15),t._v(" "),a("p",[t._v("这俩基本类型由 LLVM 支持高效地实现。即使在不支持 128 字节整数的平台上,它们都是可用的,且可像其他整数类型那样使用。")]),t._v(" "),a("p",[t._v("这俩基本类型在那些需要高效使用大整数的算法中非常有用,如某些加密算法。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"f-最新功能"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#f-最新功能","aria-hidden":"true"}},[this._v("#")]),this._v(" F - 最新功能")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"字段初始化缩写"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#字段初始化缩写","aria-hidden":"true"}},[this._v("#")]),this._v(" 字段初始化缩写")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们可以通过具名字段来初始化一个数据结构(结构体、枚举、联合体),如将 "),s("code",[this._v("fieldname: fieldname")]),this._v(" 缩写为 "),s("code",[this._v("fieldname")]),this._v(" 。这可以以更少的重复代码来完成一个复杂的初始化语法。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Person "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n age"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u8"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" name "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Peter"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" age "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("27")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Using full syntax:")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" peter "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Person "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" name"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" name"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" age"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" age "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" name "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Portia"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" age "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("27")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// Using field init shorthand:")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" portia "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Person "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" name"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" age "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{:?}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" portia"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"从循环中返回(-loop-)"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#从循环中返回(-loop-)","aria-hidden":"true"}},[this._v("#")]),this._v(" 从循环中返回( loop )")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("loop")]),this._v(" 的用法之一是重试一个可以操作,比如检查线程是否完成其任务。然而可能需要将该操作的结果传到其他部分代码。如果加上 "),s("code",[this._v("break")]),this._v(" 表达式来停止循环,则会从循环中断中返回:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" counter "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" result "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("loop")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n counter "),a("span",{attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" counter "),a("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("10")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("break")]),t._v(" counter "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("20")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"use-声明中的内置组"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#use-声明中的内置组","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("use")]),this._v(" 声明中的内置组")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("use")]),t._v(" 声明所支持的嵌套在这些情况下对你有帮助:简单的导入和全局导入。例如,下面的代码片段导入了 "),a("code",[t._v("bar")]),t._v(" 、 "),a("code",[t._v("Foo")]),t._v(" 、 "),a("code",[t._v("baz")]),t._v(" 中所有项和 "),a("code",[t._v("Bar")]),t._v(" :")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#![allow(unused_imports, dead_code)]")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" foo "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" bar "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Foo "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" baz "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" quux "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Bar "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" foo"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n bar"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Foo"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n baz"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" quux"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Bar"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"范围包含"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#范围包含","aria-hidden":"true"}},[this._v("#")]),this._v(" 范围包含")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("先前,当在表达式中使用范围( "),a("code",[t._v("..")]),t._v(" 或 "),a("code",[t._v("...")]),t._v(" )时,其必须使用排除上界的 "),a("code",[t._v("..")]),t._v(" ,而在模式中,则要使用包含上界的 "),a("code",[t._v("...")]),t._v(" 。而现在,"),a("code",[t._v("..=")]),t._v(" 可作为语法用于表达式或范围上下文件中的包含范围中。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("10")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" i "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}: low"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token number"}},[t._v("6")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("10")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}: high"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _ "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}: out of range"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("...")]),this._v(" 语法也可用在匹配语法中,但不能用于表达式中,而 "),s("code",[this._v("..=")]),this._v(" 则二者皆可。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"_128-字节的整数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_128-字节的整数","aria-hidden":"true"}},[this._v("#")]),this._v(" 128 字节的整数")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[s("code",[this._v("u128")]),this._v(": 一个在 [0, 2^128 - 1] 范围内的 128 字节的无符号整数")]),this._v(" "),s("li",[s("code",[this._v("i128")]),this._v(": 一个在 [-(2^127), 2^127 - 1] 范围内的有符号整数")])])}],!1,null,null,null);e.options.__file="appendix-06-newest-features.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/110.a106958d.js b/src/.vuepress/dist/assets/js/110.a106958d.js
new file mode 100644
index 0000000..53490e1
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/110.a106958d.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[110],{237:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch20-05-sending-requests-via-channels.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch20-05-sending-requests-via-channels.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 2e269ff82193fd65df8a87c06561d74b51ac02f7")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),n("p",[t._v("如果尝试检查代码,会得到这个错误:")]),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._m(21),t._v(" "),t._m(22),t._v(" "),n("p",[t._v("通过这些修改,代码可以编译了!我们做到了!")]),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),n("p",[t._v("理论上这段代码应该能够编译。不幸的是,Rust 编译器仍不够完美,会给出如下错误:")]),t._v(" "),t._m(35),t._m(36),t._v(" "),t._m(37),t._v(" "),n("p",[t._v("将来列表 20-20 中的代码应该能够正常工作。Rust 仍在努力改进提升编译器。有很多像你一样的人正在修复这个以及其他问题!当你结束了本书的阅读,我们希望看到你也成为他们中的一员。")]),t._v(" "),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),n("p",[t._v("这是非常狡猾且复杂的手段。无需过分担心他们并不是非常有道理;总有一天,这一切将是毫无必要的。")]),t._v(" "),t._m(45),t._v(" "),t._m(46),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"使用通道向线程发送请求"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用通道向线程发送请求","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用通道向线程发送请求")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("下一个需要解决的问题是(线程中的)闭包完全没有做任何工作。我们一直在绕过获取 "),s("code",[this._v("execute")]),this._v(" 方法中实际期望执行的闭包的问题,不过看起来在创建 "),s("code",[this._v("ThreadPool")]),this._v(" 时就需要知道实际的闭包。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不过考虑一下真正需要做的:我们希望刚创建的 "),s("code",[this._v("Worker")]),this._v(" 结构体能够从 "),s("code",[this._v("ThreadPool")]),this._v(" 的队列中获取任务,并在线程中执行他们。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在第十六章中,我们学习了通道。通道是一个沟通两个线程的良好手段,对于这个例子来说则是绝佳的。通道将充当任务队列的作用,"),s("code",[this._v("execute")]),this._v(" 将通过 "),s("code",[this._v("ThreadPool")]),this._v(" 向其中线程正在寻找工作的 "),s("code",[this._v("Worker")]),this._v(" 实例发送任务。如下是这个计划:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ol",[n("li",[n("code",[t._v("ThreadPool")]),t._v(" 会创建一个通道并充当发送端。")]),t._v(" "),n("li",[t._v("每个 "),n("code",[t._v("Worker")]),t._v(" 将会充当通道的接收端。")]),t._v(" "),n("li",[t._v("新建一个 "),n("code",[t._v("Job")]),t._v(" 结构体来存放用于向通道中发送的闭包。")]),t._v(" "),n("li",[n("code",[t._v("ThreadPool")]),t._v(" 的 "),n("code",[t._v("execute")]),t._v(" 方法会在发送端发出期望执行的任务。")]),t._v(" "),n("li",[t._v("在线程中,"),n("code",[t._v("Worker")]),t._v(" 会遍历通道的接收端并执行任何接收到的任务。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们以在 "),s("code",[this._v("ThreadPool::new")]),this._v(" 中创建通道并让 "),s("code",[this._v("ThreadPool")]),this._v(" 实例充当发送端开始,如列表 20-16 所示。"),s("code",[this._v("Job")]),this._v(" 是将在通道中发出的类型;目前它是一个没有任何内容的结构体:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Worker"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Sender"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Job"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Job"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("channel")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" workers "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Vec"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("with_capacity")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" id "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("size "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("push")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Worker"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("JoinHandle"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" thread "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("spawn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# id"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 20-16:修改 "),s("code",[this._v("ThreadPool")]),this._v(" 来储存一个发送 "),s("code",[this._v("Job")]),this._v(" 实例的通道发送端")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 "),s("code",[this._v("ThreadPool::new")]),this._v(" 中,新建了一个通道,并接着让线程池在接收端等待。这段代码能够编译,不过仍有警告。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在线程池创建每个 worker 时将通道的接收端传递给他们。须知我们希望在 worker 所分配的线程中使用通道的接收端,所以将在闭包中引用 "),s("code",[this._v("receiver")]),this._v(" 参数。列表 20-17 中展示的代码还不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("impl ThreadPool {\n // ...snip...\n pub fn new(size: usize) -> ThreadPool {\n assert!(size > 0);\n\n let (sender, receiver) = mpsc::channel();\n\n let mut workers = Vec::with_capacity(size);\n\n for id in 0..size {\n workers.push(Worker::new(id, receiver));\n }\n\n ThreadPool {\n workers,\n sender,\n }\n }\n // ...snip...\n}\n\n// ...snip...\n\nimpl Worker {\n fn new(id: usize, receiver: mpsc::Receiver) -> Worker {\n let thread = thread::spawn(|| {\n receiver;\n });\n\n Worker {\n id,\n thread,\n }\n }\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 20-17:将通道的接收端传递给 worker")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这是一些小而直观的修改:将通道的接收端传递进了 "),s("code",[this._v("Worker::new")]),this._v(",并接着在闭包中使用他们。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo check\n Compiling hello v0.1.0 (file:///projects/hello)\nerror[E0382]: use of moved value: `receiver`\n --\x3e src/lib.rs:27:42\n |\n27 | workers.push(Worker::new(id, receiver));\n | ^^^^^^^^ value moved here in\n previous iteration of loop\n |\n = note: move occurs because `receiver` has type\n `std::sync::mpsc::Receiver`, which does not implement the `Copy` trait\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这些代码还不能编译的原因如上因为它尝试将 "),n("code",[t._v("receiver")]),t._v(" 传递给多个 "),n("code",[t._v("Worker")]),t._v(" 实例。回忆第十六章,Rust 所提供的通道实现是多"),n("strong",[t._v("生产者")]),t._v(",单"),n("strong",[t._v("消费者")]),t._v("的,所以不能简单的克隆通道的消费端来解决问题。即便可以我们也不希望克隆消费端;在所有的 worker 中共享单一 "),n("code",[t._v("receiver")]),t._v(" 才是我们希望的在线程间分发任务的机制。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("另外,从通道队列中取出任务涉及到修改 "),s("code",[this._v("receiver")]),this._v(",所以这些线程需要一个能安全的共享和修改 "),s("code",[this._v("receiver")]),this._v(" 的方式。如果修改不是线程安全的,则可能遇到竞争状态,例如两个线程因同时在队列中取出相同的任务并执行了相同的工作。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("所以回忆一下第十六章讨论的线程安全智能指针,为了在多个线程间共享所有权并允许线程修改其值,需要使用 "),s("code",[this._v("Arc>")]),this._v("。"),s("code",[this._v("Arc")]),this._v(" 使得多个 worker 拥有接收端,而 "),s("code",[this._v("Mutex")]),this._v(" 则确保一次只有一个 worker 能从接收端得到任务。列表 20-18 展示了所做的修改:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Arc"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Mutex"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Worker"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Sender"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Job"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Job"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("channel")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" receiver "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Arc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Mutex"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" workers "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Vec"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("with_capacity")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" id "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("size "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("push")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Worker"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("clone")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("JoinHandle"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Arc"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Mutex"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Receiver"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Job"),n("span",{attrs:{class:"token operator"}},[t._v(">>")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" thread "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("spawn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# id"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 20-18:使用 "),s("code",[this._v("Arc")]),this._v(" 和 "),s("code",[this._v("Mutex")]),this._v(" 在 worker 间共享通道的接收端")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 "),n("code",[t._v("ThreadPool::new")]),t._v(" 中,将通道的接收端放入一个 "),n("code",[t._v("Arc")]),t._v(" 和一个 "),n("code",[t._v("Mutex")]),t._v(" 中。对于每一个新 worker,则克隆 "),n("code",[t._v("Arc")]),t._v(" 来增加引用计数,如此这些 worker 就可以共享接收端的所有权了。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("最好让我们实现 "),n("code",[t._v("ThreadPool")]),t._v(" 上的 "),n("code",[t._v("execute")]),t._v(" 方法。同时也要修改 "),n("code",[t._v("Job")]),t._v(" 结构体:它将不再是结构体,"),n("code",[t._v("Job")]),t._v(" 将是一个有着 "),n("code",[t._v("execute")]),t._v(" 接收到的闭包类型的 trait 对象的类型别名。我们讨论过类型别名如何将长的类型变短,现在就这种情况!看一看列表 20-19:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# workers"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Worker"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Sender"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Job"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sync"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Job "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Box"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" ThreadPool "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" execute"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" F"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v("\n F"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" job "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Box"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sender"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("send")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("job"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 20-19:为存放每一个闭包的 "),s("code",[this._v("Box")]),this._v(" 创建一个 "),s("code",[this._v("Job")]),this._v(" 类型别名,接着在通道中发出")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在使用 "),n("code",[t._v("execute")]),t._v(" 得到的闭包新建 "),n("code",[t._v("Job")]),t._v(" 实例之后,将这些任务从通道的发送端发出。这里调用 "),n("code",[t._v("send")]),t._v(" 上的 "),n("code",[t._v("unwrap")]),t._v(",因为如果接收端停止接收新消息则发送可能会失败,这可能发生于我们停止了所有的执行线程。不过目前这是不可能的,因为只要线程池存在他们就会一直执行。使用 "),n("code",[t._v("unwrap")]),t._v(" 是因为我们知道失败不可能发生,即便编译器不这么认为,正如第九章讨论的这是 "),n("code",[t._v("unwrap")]),t._v(" 的一个恰当用法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("那我们结束了吗?不完全是!在 worker 中,传递给 "),s("code",[this._v("thread::spawn")]),this._v(" 的闭包仍然还只是"),s("strong",[this._v("引用")]),this._v("了通道的接收端。但是我们需要闭包一直循环,向通道的接收端请求任务,并在得到任务时执行他们。如列表 20-20 对 "),s("code",[this._v("Worker::new")]),this._v(" 做出修改:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Arc"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Mutex"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Receiver"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Job"),n("span",{attrs:{class:"token operator"}},[t._v(">>")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" thread "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("spawn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("move")]),t._v(" "),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("loop")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" job "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("lock")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("recv")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Worker {} got a job; executing."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" id"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v("job"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n id"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 20-20: 在 worker 线程中接收并执行任务")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里,首先在 "),n("code",[t._v("receiver")]),t._v(" 上调用了 "),n("code",[t._v("lock")]),t._v(" 来获取互斥器,接着 "),n("code",[t._v("unwrap")]),t._v(" 在出现任何错误时 panic。如果互斥器处于一种叫做"),n("strong",[t._v("被污染")]),t._v("("),n("em",[t._v("poisoned")]),t._v(")的状态时获取锁肯能会失败,这可能发生于其他线程在持有锁时 panic 了并没有释放锁。如果当前线程因为这个原因不能得到所,调用 "),n("code",[t._v("unwrap")]),t._v(" 使其 panic 也是正确的行为。如果你觉得有意义的话请随意将 "),n("code",[t._v("unwrap")]),t._v(" 改为带有错误信息的 "),n("code",[t._v("expect")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如果锁定了互斥器,接着调用 "),n("code",[t._v("recv")]),t._v(" 从通道中接收 "),n("code",[t._v("Job")]),t._v("。最后的 "),n("code",[t._v("unwrap")]),t._v(" 也绕过了一些错误,"),n("code",[t._v("recv")]),t._v(" 在通道的发送端关闭时会返回 "),n("code",[t._v("Err")]),t._v(",类似于 "),n("code",[t._v("send")]),t._v(" 在接收端关闭时返回 "),n("code",[t._v("Err")]),t._v(" 一样。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("调用 "),s("code",[this._v("recv")]),this._v(" 的代码块;也就是说,它还没有任务,这个线程会等待直到有可用的任务。"),s("code",[this._v("Mutex")]),this._v(" 确保一次只有一个 "),s("code",[this._v("Worker")]),this._v(" 线程尝试请求任务。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0161]: cannot move a value of type std::ops::FnOnce() +\nstd::marker::Send: the size of std::ops::FnOnce() + std::marker::Send cannot be\nstatically determined\n --\x3e src/lib.rs:63:17\n |\n63 | (*job)();\n | ^^^^^^\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这个错误非常的神秘,因为这个问题本身就很神秘。为了调用储存在 "),n("code",[t._v("Box")]),t._v(" (这正是 "),n("code",[t._v("Job")]),t._v(" 别名的类型)中的 "),n("code",[t._v("FnOnce")]),t._v(" 闭包,该闭包需要能将自己移动出 "),n("code",[t._v("Box")]),t._v(",因为当调用这个闭包时,它获取 "),n("code",[t._v("self")]),t._v(" 的所有权。通常来说,将值移动出 "),n("code",[t._v("Box")]),t._v(" 是不被允许的,因为 Rust 不知道 "),n("code",[t._v("Box")]),t._v(" 中的值将会有多大;回忆第十五章能够正常使用 "),n("code",[t._v("Box")]),t._v(" 是因为我们将未知大小的值储存进 "),n("code",[t._v("Box")]),t._v(" 从而得到已知大小的值。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("第十七章曾见过,列表 17-15 中有使用了 "),n("code",[t._v("self: Box")]),t._v(" 语法的方法,它获取了储存在 "),n("code",[t._v("Box")]),t._v(" 中的 "),n("code",[t._v("Self")]),t._v(" 值的所有权。这正是我们希望做的,然而不幸的是 Rust 调用闭包的那部分实现并没有使用 "),n("code",[t._v("self: Box")]),t._v("。所以这里 Rust 也不知道它可以使用 "),n("code",[t._v("self: Box")]),t._v(" 来获取闭包的所有权并将闭包移动出 "),n("code",[t._v("Box")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("不过目前让我们绕过这个问题。所幸有一个技巧可以显式的告诉 Rust 我们处于可以获取使用 "),n("code",[t._v("self: Box")]),t._v(" 的 "),n("code",[t._v("Box")]),t._v(" 中值的所有权的状态,而一旦获取了闭包的所有权就可以调用它了。这涉及到定义一个新 trait,它带有一个在签名中使用 "),n("code",[t._v("self: Box")]),t._v(" 的方法 "),n("code",[t._v("call_box")]),t._v(",为任何实现了 "),n("code",[t._v("FnOnce()")]),t._v(" 的类型定义这个 trait,修改类型别名来使用这个新 trait,并修改 "),n("code",[t._v("Worker")]),t._v(" 使用 "),n("code",[t._v("call_box")]),t._v(" 方法。这些修改如列表 20-21 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" FnBox "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("call_box")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Box"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Self"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("FnOnce")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" FnBox "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" F "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("call_box")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Box"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("F"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Job "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Box"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("FnBox "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Send "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token comment"}},[t._v("// ...snip...")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Arc"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Mutex"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("mpsc"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Receiver"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Job"),n("span",{attrs:{class:"token operator"}},[t._v(">>")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" thread "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("spawn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("move")]),t._v(" "),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("loop")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" job "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" receiver"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("lock")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("recv")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("unwrap")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Worker {} got a job; executing."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" id"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n job"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("call_box")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n Worker "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n id"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 20-21:新增一个 trait "),s("code",[this._v("FnBox")]),this._v(" 来绕过当前 "),s("code",[this._v("Box")]),this._v(" 的限制")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("首先,新建了一个叫做 "),n("code",[t._v("FnBox")]),t._v(" 的 trait。这个 trait 有一个方法 "),n("code",[t._v("call_box")]),t._v(",它类似于其他 "),n("code",[t._v("Fn*")]),t._v(" trait 中的 "),n("code",[t._v("call")]),t._v(" 方法,除了它获取 "),n("code",[t._v("self: Box")]),t._v(" 以便获取 "),n("code",[t._v("self")]),t._v(" 的所有权并将值从 "),n("code",[t._v("Box")]),t._v(" 中移动出来。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("现在我们希望 "),n("code",[t._v("Job")]),t._v(" 类型别名是任何实现了新 trait "),n("code",[t._v("FnBox")]),t._v(" 的 "),n("code",[t._v("Box")]),t._v(",而不是 "),n("code",[t._v("FnOnce()")]),t._v("。这允许我们在得到 "),n("code",[t._v("Job")]),t._v(" 值时使用 "),n("code",[t._v("Worker")]),t._v(" 中的 "),n("code",[t._v("call_box")]),t._v("。因为我们为任何 "),n("code",[t._v("FnOnce()")]),t._v(" 闭包都实现了 "),n("code",[t._v("FnBox")]),t._v(" trait,无需对实际在通道中发出的值做任何修改。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("最后,对于 "),s("code",[this._v("Worker::new")]),this._v(" 的线程中所运行的闭包,调用 "),s("code",[this._v("call_box")]),this._v(" 而不是直接执行闭包。现在 Rust 就能够理解我们的行为是正确的了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("通过这些技巧,线程池处于可以运行的状态了!执行 "),s("code",[this._v("cargo run")]),this._v(" 并发起一些请求:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling hello v0.1.0 (file:///projects/hello)\nwarning: field is never used: `workers`\n --\x3e src/lib.rs:7:5\n |\n7 | workers: Vec,\n | ^^^^^^^^^^^^^^^^^^^^\n |\n = note: #[warn(dead_code)] on by default\n\nwarning: field is never used: `id`\n --\x3e src/lib.rs:61:5\n |\n61 | id: usize,\n | ^^^^^^^^^\n |\n = note: #[warn(dead_code)] on by default\n\nwarning: field is never used: `thread`\n --\x3e src/lib.rs:62:5\n |\n62 | thread: thread::JoinHandle<()>,\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = note: #[warn(dead_code)] on by default\n\n Finished dev [unoptimized + debuginfo] target(s) in 0.99 secs\n Running `target/debug/hello`\n Worker 0 got a job; executing.\nWorker 2 got a job; executing.\nWorker 1 got a job; executing.\nWorker 3 got a job; executing.\nWorker 0 got a job; executing.\nWorker 2 got a job; executing.\nWorker 1 got a job; executing.\nWorker 3 got a job; executing.\nWorker 0 got a job; executing.\nWorker 2 got a job; executing.\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("成功了!现在我们有了一个可以异步执行连接的线程池!它绝不会创建超过四个线程,所以当 server 收到大量请求时系统也不会负担过重。如果请求 "),s("code",[this._v("/sleep")]),this._v(",server 也能够通过另外一个线程处理其他请求。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("那么这些警告怎么办呢?难道我们没有使用 "),n("code",[t._v("workers")]),t._v("、"),n("code",[t._v("id")]),t._v(" 和 "),n("code",[t._v("thread")]),t._v(" 字段吗?好吧,目前我们用了所有这些字段存放了一些数据,不过当设置线程池并开始执行代码在通道中向线程发送任务时,我们并没有对数据"),n("strong",[t._v("进行")]),t._v("任何实际的操作。但是如果不存放这些值,他们将会离开作用域:比如,如果不将 "),n("code",[t._v("Vec")]),t._v(" 值作为 "),n("code",[t._v("ThreadPool")]),t._v(" 的一部分返回,这个 vector 在 "),n("code",[t._v("ThreadPool::new")]),t._v(" 的结尾就会被清理。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("那么这些警告有错吗?从某种角度上讲是的,这些警告是错误的,因为我们使用这些字段储存一直需要的数据。从另一种角度来说也不对:使用过后我们也没有做任何操作清理线程池,仅仅通过 "),s("span",{staticClass:"keystroke"},[this._v("ctrl-C")]),this._v(" 来停止程序并让操作系统为我们清理。下面让我们实现 graceful shutdown 来清理所创建的一切。")])}],!1,null,null,null);e.options.__file="ch20-05-sending-requests-via-channels.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/111.b1600e65.js b/src/.vuepress/dist/assets/js/111.b1600e65.js
new file mode 100644
index 0000000..2f03f2b
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/111.b1600e65.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[111],{230:function(t,e,s){"use strict";s.r(e);var r=s(0),n=Object(r.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/foreword.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("foreword.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit 5e085bd1add34aec03416e891751552b439dde52")])]),t._v(" "),t._m(1),t._v(" "),s("p",[t._v("比如,“系统层面”(“systems-level”)的工作,涉及内存管理、数据表示和并发等底层细节。从传统角度来看,这是一个神秘的编程领域,只为浸淫多年的极少数人所触及,也只有他们能避开那些臭名昭著的陷阱。即使谨慎的实践者,亦唯恐代码出现漏洞、崩溃或损坏。")]),t._v(" "),s("p",[t._v("Rust 破除了这些障碍,其消除了旧的陷阱并提供了伴你一路同行的友好、精良的工具。想要 “深入” 底层控制的程序员可以使用 Rust,无需冒着常见的崩溃或安全漏洞的风险,也无需学习时常改变的工具链的最新知识。其语言本身更是被设计为自然而然的引导你编写出在运行速度和内存使用上都十分高效的可靠代码。")]),t._v(" "),s("p",[t._v("已经在从事编写底层代码的程序员可以使用 Rust 来提升抱负。例如,在 Rust 中引入并行是相对低风险的操作:编译器会为你捕获经典的错误。同时你可以自信的采取更为积极的优化,而不会意外引入崩溃或漏洞。")]),t._v(" "),s("p",[t._v("但 Rust 并不局限于底层系统编程。其表现力和工效足以令人愉悦的编写出 CLI 应用、web server 和很多其他类型的代码 —— 在本书中你会看到两个简单示例。使用 Rust 能将你在一个领域中学习的技能延伸到另一个领域;你可以学习 Rust 来编写 web 应用,接着将同样的技能应用到你的 Raspberry Pi(树莓派)上。")]),t._v(" "),s("p",[t._v("本书全面介绍了 Rust 为用户赋予的能力。其内容平易近人,致力于帮助你提升 Rust 的知识,并且提升你作为程序员整体的理解与自信。那么让我们准备深入学习 Rust 吧(打开新世界的大门 😃) —— 欢迎加入 Rust 社区!")]),t._v(" "),s("p",[t._v("— Nicholas Matsakis 和 Aaron Turon")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"前言"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#前言","aria-hidden":"true"}},[this._v("#")]),this._v(" 前言")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("虽然不是那么明显,但 Rust 程序设计语言的本质在于 "),e("strong",[this._v("赋能")]),this._v("("),e("em",[this._v("empowerment")]),this._v("):无论你现在编写的是何种代码,Rust 能让你在更为广泛的编程领域走得更远,写出自信。")])}],!1,null,null,null);n.options.__file="foreword.md";e.default=n.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/112.81bf40f0.js b/src/.vuepress/dist/assets/js/112.81bf40f0.js
new file mode 100644
index 0000000..be38133
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/112.81bf40f0.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[112],{166:function(n,w,o){}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/12.c7e4d9a8.js b/src/.vuepress/dist/assets/js/12.c7e4d9a8.js
new file mode 100644
index 0000000..3498a73
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/12.c7e4d9a8.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{275:function(t,s,e){"use strict";e.r(s);var i=e(0),n=Object(i.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"content"},[s("h2",{attrs:{id:"g-rust-是如何开发的与-“nightly-rust”"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#g-rust-是如何开发的与-“nightly-rust”","aria-hidden":"true"}},[this._v("#")]),this._v(" G - Rust 是如何开发的与 “Nightly Rust”")])])}],!1,null,null,null);n.options.__file="appendix-07-nightly-rust.md";s.default=n.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/13.235bc06a.js b/src/.vuepress/dist/assets/js/13.235bc06a.js
new file mode 100644
index 0000000..13051d9
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/13.235bc06a.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{167:function(t,s,r){"use strict";r.r(s);var e=r(0),i=Object(e.a)({},function(){var t=this,s=t.$createElement,r=t._self._c||s;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),r("blockquote",[r("p",[r("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch00-00-introduction.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch00-00-introduction.md"),r("OutboundLink")],1),t._v(" "),r("br"),t._v("\ncommit 0aa307c7d79d2cbf83cdf5d47780b2904e9cb03f")])]),t._v(" "),r("blockquote",[r("p",[t._v("注意:本书的版本与出版的 "),r("a",{attrs:{href:"https://nostarch.com/rust",target:"_blank",rel:"noopener noreferrer"}},[t._v("The Rust Programming Language"),r("OutboundLink")],1),t._v("\n和电子版的 "),r("a",{attrs:{href:"https://nostarch.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("No Starch Press"),r("OutboundLink")],1),t._v(" 一致")])]),t._v(" "),r("p",[t._v("欢迎阅读 “Rust 程序设计语言”,一本介绍 Rust 的书。Rust 程序设计语言能帮助你编写更快、更可靠的软件。在编程语言设计中,高层工程学和底层控制往往不能兼得;Rust 则试图挑战这一矛盾。通过权衡强大的技术能力与优秀的开发体验,Rust 允许你控制底层细节(比如内存使用),并免受以往进行此类控制所经受的所有烦恼。")]),t._v(" "),t._m(1),t._v(" "),r("p",[t._v("Rust 因多种原因适用于很多开发者。让我们讨论几个最重要的群体。")]),t._v(" "),t._m(2),t._v(" "),r("p",[t._v("Rust 被证明是可用于大型的、拥有不同层次系统编程知识的开发者团队间协作的高效工具。底层代码中容易出现种种隐晦的 bug,在其他编程语言中,只能通过大量的测试和经验丰富的开发者细心的代码评审来捕获它们。在 Rust 中,编译器充当了守门员的角色,它拒绝编译存在这些难以捕获的 bug 的代码,这其中包括并发 bug。通过与编译器合作,团队将更多的时间聚焦在程序逻辑上,而不是追踪 bug。")]),t._v(" "),r("p",[t._v("Rust 也为系统编程世界带来了现代化的开发工具:")]),t._v(" "),t._m(3),t._v(" "),r("p",[t._v("通过使用 Rust 生态系统中的这些和其他工具,开发者可以在编写系统层面代码时保持高生产力。")]),t._v(" "),t._m(4),t._v(" "),r("p",[t._v("Rust 适用于学生和任何对学习操作系统概念感兴趣的人。通过 Rust,很多人已经了解了像操作系统开发这样的主题。社区非常欢迎并乐于解答学生们的问题。通过类似于本书这样的努力,Rust 团队希望更多人了解操作系统的概念,特别是编程新手。")]),t._v(" "),t._m(5),t._v(" "),r("p",[t._v("数以百计的公司,无论规模大小,正将 Rust 用于生产环境中的多种任务。这些任务包括命令行工具、web 服务、DevOps 工具、嵌入式设备、音视频分析与转码、数字货币(cryptocurrencies)、生物信息学(bioinformatics)、搜索引擎、物联网(internet of things, IOT)程序、机器学习,甚至还包括 Firefox 浏览器的大部分内容。")]),t._v(" "),t._m(6),t._v(" "),r("p",[t._v("Rust 适用于希望构建 Rust 编程语言、社区、开发工具和库的开发者。我们期望你为 Rust 语言做贡献。")]),t._v(" "),t._m(7),t._v(" "),r("p",[t._v("Rust 适用于追求编程语言的速度与稳定性的开发者。所谓速度,是指你用 Rust 开发出的程序运行速度,以及 Rust 提供的程序开发速度。Rust 的编译器检查确保了增加功能和重构代码时的稳定性。这与缺少这些检查的语言形成鲜明对比,开发者通常害怕修改那些脆弱的遗留代码。通过力求零开销抽象(zero-cost abstractions),高层级的特性被编译为与手写一样快的底层代码,Rust 致力于使安全的代码也同样快速。")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),r("p",[t._v("总体来说,本书假设你会从头到尾顺序阅读。稍后的章节建立在之前章节概念的基础上,同时之前的章节可能不会深入讨论某个主题的细节;通常稍后的章节会重新讨论这些主题。")]),t._v(" "),r("p",[t._v("你会在本书中发现两类章节:概念章节和项目章节。在概念章节中,我们学习 Rust 的某个方面。在项目章节中,我们应用目前所学的知识一同构建小的程序。第二、十二和二十章是项目章节;其余都是概念章节。")]),t._v(" "),r("p",[t._v("第一章介绍如何安装 Rust,如何编写 Hello, world! 程序,以及如何使用 Rust 的包管理器和构建工具 Cargo。第二章是 Rust 语言的实战介绍。我们会站在较高的层次介绍一些的概念,在稍后的章节种会做详细介绍。如果你希望立刻就动手实践一下,第二章正好适合你。开始阅读时,你甚至可能希望略过第三章,它介绍了 Rust 中类似其他编程语言中的功能,并直接阅读第四章学习 Rust 的所有权系统。然而,如果你是特别重视细节的学习者,并倾向于在继续之前学习每一个细节,你可能希望略过第二章并直接阅读第三章,并在想要构建项目来实践这些细节时再回来阅读第二章。")]),t._v(" "),t._m(12),t._v(" "),r("p",[t._v("第七章,你会学习 Rust 的模块系统和私有性规则来组织代码和公有应用程序接口(Application Programming Interface, API)。第八章讨论了一些标准库提供的通用集合数据结构,比如 vector、字符串和哈希 map。第九章探索了 Rust 的错误处理哲学和技术。")]),t._v(" "),t._m(13),t._v(" "),r("p",[t._v("第十三章探索了闭包和迭代器:Rust 中来自函数式编程语言的功能。第十四章会更深层次的理解 Cargo 并讨论向他人分享库的最佳实践。第十五章讨论标准库提供的智能指针以及启用这些功能的 trait。")]),t._v(" "),r("p",[t._v("第十六章会学习不同的并发编程模型,并讨论 Rust 如何助你无畏的编写多线程程序。第十七章着眼于比较 Rust 风格与你可能熟悉的面向对象编程原则。")]),t._v(" "),r("p",[t._v("第十八章是一个模式与模式匹配的参考章节,他们是在整个 Rust 程序中表达意图的强大方式。第十九章是一个高级主题大杂烩,包括 unsafe Rust、宏和更多关于生命周期、 trait、类型、函数和闭包的内容。")]),t._v(" "),r("p",[t._v("第二十章将会完成一个项目,我们会实现一个底层的、多线程的 web server!")]),t._v(" "),r("p",[t._v("最后是一些附录,包含了一些关于语言的参考风格格式的实用信息。附录 A 介绍了 Rust 的关键字。附录 B 介绍 Rust 的运算符和符号。附录 C 介绍标准库提供的派生 trait。附录 D 涉及了一些有用的开发工具,附录 E 介绍了 Rust 的不同版本。")]),t._v(" "),r("p",[t._v("怎样阅读本书都不会有任何问题:如果你希望略过一些内容,请继续!如果你发现疑惑可能会再跳回之前的章节。请随意阅读。")]),t._v(" "),t._m(14),t._v(" "),r("p",[t._v("学习 Rust 的过程中一个重要的部分是学习如何阅读编译器提供的错误信息:它们会指导你编写出能工作的代码。为此,我们会提供很多不能编译的示例代码,以及各个情况下编译器会展示的错误信息。请注意如果随便输入并运行随机的示例代码,它们可能无法编译!请确保阅读任何你尝试运行的示例周围的内容,检视他们是否有意写错。Ferris 也会帮助你区别那些有意无法工作的代码:")]),t._v(" "),t._m(15),t._v(" "),r("p",[t._v("在大部分情况,我们会指引你将任何不能编译的代码纠正为正确版本。")]),t._v(" "),t._m(16),t._v(" "),r("p",[t._v("生成本书的源码可以在 "),r("a",{attrs:{href:"https://github.com/rust-lang/book/tree/master/src",target:"_blank",rel:"noopener noreferrer"}},[t._v("GitHub"),r("OutboundLink")],1),t._v(" 上找到。")]),t._v(" "),r("blockquote",[r("p",[t._v("译者注:本译本的 "),r("a",{attrs:{href:"https://github.com/KaiserY/trpl-zh-cn",target:"_blank",rel:"noopener noreferrer"}},[t._v("GitHub 仓库"),r("OutboundLink")],1),t._v(",欢迎 Issue 和 PR 😃")])])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"介绍"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#介绍","aria-hidden":"true"}},[this._v("#")]),this._v(" 介绍")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"谁会使用-rust"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#谁会使用-rust","aria-hidden":"true"}},[this._v("#")]),this._v(" 谁会使用 Rust")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"开发者团队"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#开发者团队","aria-hidden":"true"}},[this._v("#")]),this._v(" 开发者团队")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[this._v("Cargo,内置的依赖管理器和构建工具,它能轻松增加、编译和管理依赖,并使其在 Rust 生态系统中保持一致。")]),this._v(" "),s("li",[this._v("Rustfmt 确保开发者遵循一致的代码风格。")]),this._v(" "),s("li",[this._v("Rust Language Server 为集成开发环境(IDE)提供了强大的代码补全和内联错误信息功能。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"学生"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#学生","aria-hidden":"true"}},[this._v("#")]),this._v(" 学生")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"公司"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#公司","aria-hidden":"true"}},[this._v("#")]),this._v(" 公司")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"开源开发者"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#开源开发者","aria-hidden":"true"}},[this._v("#")]),this._v(" 开源开发者")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"重视速度和稳定性的开发者"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#重视速度和稳定性的开发者","aria-hidden":"true"}},[this._v("#")]),this._v(" 重视速度和稳定性的开发者")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 语言也希望能支持很多其他用户,这里提及的只是最大的利益相关者。总的来讲,Rust 最重要的目标是消除数十年来程序员不得不做的权衡:安全 "),s("strong",[this._v("与")]),this._v(" 生产力、速度 "),s("strong",[this._v("与")]),this._v(" 工程学。请尝试 Rust,看看这个选择是否适合你。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"本书是写给谁的"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#本书是写给谁的","aria-hidden":"true"}},[this._v("#")]),this._v(" 本书是写给谁的")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("本书假设你已经使用其他编程语言编写过代码,但并不假设你使用的是何种语言。我们尝试使这些材料能广泛的适用于来自很多不同编程背景的开发者。我们不会花费很多时间讨论编程 "),s("strong",[this._v("是")]),this._v(" 什么或者如何理解它。如果编程对于你来说是完全陌生的,你最好先阅读专门介绍编程的书籍。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"如何阅读本书"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#如何阅读本书","aria-hidden":"true"}},[this._v("#")]),this._v(" 如何阅读本书")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第五章讨论结构体和方法,第六章介绍枚举、"),s("code",[this._v("match")]),this._v(" 表达式和 "),s("code",[this._v("if let")]),this._v(" 控制流结构。在 Rust 中,你将使用结构体和枚举创建自定义类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第十章深入介绍泛型、trait 和生命周期,他们提供了定义出适用于多种类型的代码的能力。第十一章全部关于测试,即使 Rust 有安全保证,也需要测试确保程序逻辑正确。第十二章,我们构建了属于自己的在文件中搜索文本的命令行工具 "),s("code",[this._v("grep")]),this._v(" 的子集功能实现。为此会利用之前章节讨论的很多概念。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{attrs:{id:"ferris"}})])},function(){var t=this,s=t.$createElement,r=t._self._c||s;return r("table",[r("thead",[r("tr",[r("th",[t._v("Ferris")]),t._v(" "),r("th",[t._v("意义")])])]),t._v(" "),r("tbody",[r("tr",[r("td",[r("img",{staticClass:"ferris-explain",attrs:{src:"img/ferris/does_not_compile.svg"}})]),t._v(" "),r("td",[t._v("这些代码不能编译!")])]),t._v(" "),r("tr",[r("td",[r("img",{staticClass:"ferris-explain",attrs:{src:"img/ferris/panics.svg"}})]),t._v(" "),r("td",[t._v("这些代码会 panic!")])]),t._v(" "),r("tr",[r("td",[r("img",{staticClass:"ferris-explain",attrs:{src:"img/ferris/unsafe.svg"}})]),t._v(" "),r("td",[t._v("这些代码块包含不安全(unsafe)代码")])]),t._v(" "),r("tr",[r("td",[r("img",{staticClass:"ferris-explain",attrs:{src:"img/ferris/not_desired_behavior.svg"}})]),t._v(" "),r("td",[t._v("这些代码不会产生期望的行为。")])])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"源代码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#源代码","aria-hidden":"true"}},[this._v("#")]),this._v(" 源代码")])}],!1,null,null,null);i.options.__file="ch00-00-introduction.md";s.default=i.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/14.8b8a21b7.js b/src/.vuepress/dist/assets/js/14.8b8a21b7.js
new file mode 100644
index 0000000..1993f94
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/14.8b8a21b7.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{274:function(t,e,r){"use strict";r.r(e);var n=r(0),s=Object(n.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),r("blockquote",[r("p",[r("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch01-00-getting-started.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch01-00-getting-started.md"),r("OutboundLink")],1),t._v(" "),r("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),r("p",[t._v("让我们开始 Rust 之旅!有很多内容需要学习,但每次旅程总有起点。在本章中,我们会讨论:")]),t._v(" "),t._m(1)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"入门指南"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#入门指南","aria-hidden":"true"}},[this._v("#")]),this._v(" 入门指南")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ul",[r("li",[t._v("在 Linux、macOS 和 Windows 上安装 Rust")]),t._v(" "),r("li",[t._v("编写一个打印 "),r("code",[t._v("Hello, world!")]),t._v(" 的程序")]),t._v(" "),r("li",[t._v("使用 Rust 的包管理器和构建系统 "),r("code",[t._v("cargo")])])])}],!1,null,null,null);s.options.__file="ch01-00-getting-started.md";e.default=s.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/15.83a607fc.js b/src/.vuepress/dist/assets/js/15.83a607fc.js
new file mode 100644
index 0000000..4c0f75a
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/15.83a607fc.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{273:function(t,e,s){"use strict";s.r(e);var r=s(0),a=Object(r.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch01-01-installation.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch01-01-installation.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),s("blockquote",[s("p",[t._v("注意:如果你出于某些理由倾向于不使用 "),s("code",[t._v("rustup")]),t._v(",请到 "),s("a",{attrs:{href:"https://www.rust-lang.org/install.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("Rust 安装页面"),s("OutboundLink")],1),t._v(" 查看其它安装选项。")])]),t._v(" "),s("p",[t._v("接下来的步骤会安装最新的稳定版 Rust 编译器。Rust 的稳定性确保本书所有示例在最新版本的 Rust 中能够继续编译。不同版本的输出可能略有不同,因为 Rust 经常改进错误信息和警告。也就是说,任何通过这些步骤安装的最新稳定版 Rust,都应该能正常运行本书中的内容。")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),s("p",[t._v("如果你使用 Linux 或 macOS,打开终端并输入如下命令:")]),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),s("p",[t._v("如果你愿意,可在运行前下载并检查该脚本。")]),t._v(" "),s("p",[t._v("此安装脚本自动将 Rust 加入系统 PATH 环境变量中,在下一次登录时生效。如果你希望立刻就开始使用 Rust 而不重启终端,在 shell 中运行如下命令,手动将 Rust 加入系统 PATH 变量中:")]),t._v(" "),t._m(7),t._m(8),t._v(" "),t._m(9),s("p",[t._v("另外,你需要一个某种类型的链接器(linker)。很有可能已经安装,不过当你尝试编译 Rust 程序时,却有错误指出无法执行链接器,这意味着你的系统上没有安装链接器,你需要自行安装一个。C 编译器通常带有正确的链接器。请查看你使用平台的文档,了解如何安装 C 编译器。并且,一些常用的 Rust 包依赖 C 代码,也需要安装 C 编译器。因此现在安装一个是值得的。")]),t._v(" "),t._m(10),t._v(" "),s("p",[t._v("在 Windows 上,前往 "),s("a",{attrs:{href:"https://www.rust-lang.org/install.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://www.rust-lang.org/install.html"),s("OutboundLink")],1),t._v(" 并按照说明安装 Rust。在安装过程的某个步骤,你会收到一个信息说明为什么需要安装 Visual Studio 2013 或更新版本的 C++ build tools。获取这些 build tools 最方便的方法是安装 "),s("a",{attrs:{href:"https://www.visualstudio.com/downloads/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Build Tools for Visual Studio 2017"),s("OutboundLink")],1),t._v("。这个工具在 “Other Tools and Frameworks” 部分。")]),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._m(17),t._v(" "),s("p",[t._v("要检查是否正确安装了 Rust,打开 shell 并运行如下行:")]),t._v(" "),t._m(18),s("p",[t._v("你应能看到已发布的最新稳定版的版本号、提交哈希和提交日期,显示为如下格式:")]),t._v(" "),t._m(19),s("p",[t._v("如果出现这些内容,Rust 就安装成功了!如果并没有看到这些信息,并且使用的是 Windows,请检查 Rust 是否位于 "),s("code",[t._v("%PATH%")]),t._v(" 系统变量中。如果一切正确但 Rust 仍不能使用,有许多地方可以求助。最简单的是 "),s("router-link",{attrs:{to:"./irc://irc.mozilla.org/#rust"}},[t._v("irc.mozilla.org 上的 #rust IRC 频道")]),t._v(" ,可以使用 "),s("a",{attrs:{href:"http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust",target:"_blank",rel:"noopener noreferrer"}},[t._v("Mibbit"),s("OutboundLink")],1),t._v(" 来访问它。然后就能和其他 Rustacean(Rust 用户的称号,有自嘲意味)聊天并寻求帮助。其它给力的资源包括"),s("a",{attrs:{href:"https://users.rust-lang.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("用户论坛"),s("OutboundLink")],1),t._v("和 "),s("a",{attrs:{href:"http://stackoverflow.com/questions/tagged/rust",target:"_blank",rel:"noopener noreferrer"}},[t._v("Stack Overflow"),s("OutboundLink")],1),t._v("。")],1),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),s("p",[t._v("任何时候,如果你拿不准标准库中的类型或函数的用途和用法,请查阅应用程序接口(application programming interface,API)文档!")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"安装"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#安装","aria-hidden":"true"}},[this._v("#")]),this._v(" 安装")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("第一步是安装 Rust。我们通过 "),e("code",[this._v("rustup")]),this._v(" 下载 Rust,这是一个管理 Rust 版本和相关工具的命令行工具。下载时需要联网。")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("blockquote",[s("h3",{attrs:{id:"命令行标记"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#命令行标记","aria-hidden":"true"}},[t._v("#")]),t._v(" 命令行标记")]),t._v(" "),s("p",[t._v("本章和全书中,我们会展示一些在终端中使用的命令。所有需要输入到终端的行都以 "),s("code",[t._v("$")]),t._v(" 开头。但无需输入"),s("code",[t._v("$")]),t._v(";它代表每行命令的起点。不以 "),s("code",[t._v("$")]),t._v(" 起始的行通常展示之前命令的输出。另外,PowerShell 专用的示例会采用 "),s("code",[t._v(">")]),t._v(" 而不是 "),s("code",[t._v("$")]),t._v("。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"在-linux-或-macos-上安装-rustup"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#在-linux-或-macos-上安装-rustup","aria-hidden":"true"}},[this._v("#")]),this._v(" 在 Linux 或 macOS 上安装 "),e("code",[this._v("rustup")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ curl https://sh.rustup.rs -sSf | sh\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("此命令下载一个脚本并开始安装 "),e("code",[this._v("rustup")]),this._v(" 工具,这会安装最新稳定版 Rust。过程中可能会提示你输入密码。如果安装成功,将会出现如下内容:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("Rust is installed now. Great!\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ source $HOME/.cargo/env\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("或者,可以在 "),e("em",[this._v("~/.bash_profile")]),this._v(" 文件中增加如下行:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('$ export PATH="$HOME/.cargo/bin:$PATH"\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"在-windows-上安装-rustup"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#在-windows-上安装-rustup","aria-hidden":"true"}},[this._v("#")]),this._v(" 在 Windows 上安装 "),e("code",[this._v("rustup")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("本书的余下部分会使用能同时运行于 "),e("em",[this._v("cmd.exe")]),this._v(" 和 PowerShell 的命令。如果存在特定差异,我们会解释使用哪一个。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"更新和卸载"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#更新和卸载","aria-hidden":"true"}},[this._v("#")]),this._v(" 更新和卸载")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("通过 "),e("code",[this._v("rustup")]),this._v(" 安装了 Rust 之后,很容易更新到最新版本。在 shell 中运行如下更新脚本:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ rustup update\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("为了卸载 Rust 和 "),e("code",[this._v("rustup")]),this._v(",在 shell 中运行如下卸载脚本:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ rustup self uninstall\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"故障排除(troubleshooting)"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#故障排除(troubleshooting)","aria-hidden":"true"}},[this._v("#")]),this._v(" 故障排除(Troubleshooting)")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ rustc --version\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("rustc x.y.z (abcabcabc yyyy-mm-dd)\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("blockquote",[e("p",[this._v("译者:恭喜入坑!(此处应该有掌声!)")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"本地文档"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#本地文档","aria-hidden":"true"}},[this._v("#")]),this._v(" 本地文档")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("安装程序也自带一份文档的本地拷贝,可以离线阅读。运行 "),e("code",[this._v("rustup doc")]),this._v(" 在浏览器中查看本地文档。")])}],!1,null,null,null);a.options.__file="ch01-01-installation.md";e.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/16.2bf746d6.js b/src/.vuepress/dist/assets/js/16.2bf746d6.js
new file mode 100644
index 0000000..4c1e69e
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/16.2bf746d6.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[16],{271:function(t,s,e){"use strict";e.r(s);var n=e(0),a=Object(n.a)({},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[t._m(0),t._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch01-02-hello-world.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch01-02-hello-world.md"),e("OutboundLink")],1),t._v(" "),e("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),e("p",[t._v("对于 Linux 和 macOS,输入:")]),t._v(" "),t._m(6),e("p",[t._v("对于 Windows CMD,输入:")]),t._v(" "),t._m(7),e("p",[t._v("对于 Windows PowerShell,输入:")]),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._m(14),t._v(" "),e("p",[t._v("保存文件,并回到终端窗口。在 Linux 或 macOS 上,输入如下命令,编译并运行文件:")]),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),e("p",[t._v("现在,让我们回过头来仔细看看 Hello, world! 程序中到底发生了什么。这是第一块拼图:")]),t._v(" "),t._m(21),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),e("p",[t._v("这行代码完成这个简单程序的所有工作:在屏幕上打印文本。这里有四个重要的细节需要注意。首先 Rust 的缩进风格使用 4 个空格,而不是 1 个制表符(tab)。")]),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),e("p",[t._v("你刚刚运行了一个新创建的程序,那么让我们检查此过程中的每一个步骤。")]),t._v(" "),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),e("p",[t._v("在 Windows 的 CMD 上,则输入如下内容:")]),t._v(" "),t._m(36),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"hello-world"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#hello-world","aria-hidden":"true"}},[this._v("#")]),this._v(" Hello, World!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("既然安装好了 Rust,我们来编写第一个 Rust 程序。当学习一门新语言的时候,使用该语言在屏幕上打印 "),s("code",[this._v("Hello, world!")]),this._v(" 是一项传统,我们将沿用这一传统!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("blockquote",[s("p",[this._v("注意:本书假设你熟悉基本的命令行操作。Rust 对于你的编辑器、工具,以及代码位于何处并没有特定的要求,如果你更倾向于使用集成开发环境(IDE),而不是命令行,请尽管使用你喜欢的 IDE。目前很多 IDE 已经不同程度的支持 Rust;查看 IDE 文档了解更多细节。最近,Rust 团队已经致力于提供强大的 IDE 支持,而且进展飞速!")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"创建项目目录"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#创建项目目录","aria-hidden":"true"}},[this._v("#")]),this._v(" 创建项目目录")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("首先创建一个存放 Rust 代码的目录。Rust 并不关心代码的存放位置,不过对于本书的练习和项目来说,我们建议你在 home 目录中创建 "),s("em",[this._v("projects")]),this._v(" 目录,并将你的所有项目存放在这里。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("打开终端并输入如下命令创建 "),s("em",[this._v("projects")]),this._v(" 目录,并在 "),s("em",[this._v("projects")]),this._v(" 目录中为 Hello, world! 项目创建一个目录。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ mkdir ~/projects\n$ cd ~/projects\n$ mkdir hello_world\n$ cd hello_world\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-cmd extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('> mkdir "%USERPROFILE%\\projects"\n> cd /d "%USERPROFILE%\\projects"\n> mkdir hello_world\n> cd hello_world\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-powershell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-powershell"}},[s("code",[this._v("> mkdir "),s("span",{attrs:{class:"token variable"}},[this._v("$env")]),this._v(":USERPROFILE\\projects\n> cd "),s("span",{attrs:{class:"token variable"}},[this._v("$env")]),this._v(":USERPROFILE\\projects\n> mkdir hello_world\n> cd hello_world\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"编写并运行-rust-程序"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#编写并运行-rust-程序","aria-hidden":"true"}},[this._v("#")]),this._v(" 编写并运行 Rust 程序")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("接下来,新建一个源文件,命名为 "),e("em",[t._v("main.rs")]),t._v("。Rust 源文件总是以 "),e("em",[t._v(".rs")]),t._v(" 扩展名结尾。如果文件名包含多个单词,使用下划线分隔它们。例如命名为 "),e("em",[t._v("hello_world.rs")]),t._v(",而不是 "),e("em",[t._v("helloworld.rs")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在打开刚创建的 "),s("em",[this._v("main.rs")]),this._v(" 文件,输入示例 1-1 中的代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: main.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("main")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("println!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token string"}},[t._v('"Hello, world!"')]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 1-1: 一个打印 "),s("code",[this._v("Hello, world!")]),this._v(" 的程序")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ rustc main.rs\n$ ./main\nHello, world!\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 Windows 上,输入命令 "),s("code",[this._v(".\\main.exe")]),this._v(",而不是 "),s("code",[this._v("./main")]),this._v(":")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-powershell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-powershell"}},[e("code",[t._v("> rustc main"),e("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("rs\n> "),e("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\\main"),e("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exe\nHello"),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" world"),e("span",{attrs:{class:"token operator"}},[t._v("!")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不管使用何种操作系统,终端应该打印字符串 "),s("code",[this._v("Hello, world!")]),this._v("。如果没有看到这些输出,回到 “故障排除” 部分查找寻求帮助的方法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果 "),s("code",[this._v("Hello, world!")]),this._v(" 出现了,恭喜你!你已经正式编写了一个 Rust 程序。现在你成为一名 Rust 程序员,欢迎!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"分析-rust-程序"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#分析-rust-程序","aria-hidden":"true"}},[this._v("#")]),this._v(" 分析 Rust 程序")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("main")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这几行定义了一个 Rust 函数。"),s("code",[this._v("main")]),this._v(" 函数是一个特殊的函数:在可执行的 Rust 程序中,它总是最先运行的代码。第一行代码声明了一个叫做 "),s("code",[this._v("main")]),this._v(" 的函数,它没有参数也没有返回值。如果有参数的话,它们的名称应该出现在小括号中,"),s("code",[this._v("()")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("还须注意,函数体被包裹在花括号中,"),s("code",[this._v("{}")]),this._v("。Rust 要求所有函数体都要用花括号包裹起来。一般来说,将左花括号与函数声明置于同一行并以空格分隔,是良好的代码风格。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在编写本书的时候,一个叫做 "),s("code",[this._v("rustfmt")]),this._v(" 的自动格式化工具正在开发中。如果你希望在 Rust 项目中保持一种标准风格,"),s("code",[this._v("rustfmt")]),this._v(" 会将代码格式化为特定的风格。Rust 团队计划最终将该工具包含在标准 Rust 发行版中,就像 "),s("code",[this._v("rustc")]),this._v("。所以根据你阅读本书的时间,它可能已经安装到你的电脑中了!检查在线文档以了解更多细节。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 "),s("code",[this._v("main()")]),this._v(" 函数中是如下代码:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[this._v(" "),s("span",{attrs:{class:"token function"}},[this._v("println!")]),s("span",{attrs:{class:"token punctuation"}},[this._v("(")]),s("span",{attrs:{class:"token string"}},[this._v('"Hello, world!"')]),s("span",{attrs:{class:"token punctuation"}},[this._v(")")]),s("span",{attrs:{class:"token punctuation"}},[this._v(";")]),this._v("\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("第二,"),e("code",[t._v("println!")]),t._v(" 调用了一个 Rust 宏(macro)。如果是调用函数,则应输入 "),e("code",[t._v("println")]),t._v("(没有"),e("code",[t._v("!")]),t._v(")。我们将在第十九章详细讨论宏。现在你只需记住,当看到符号 "),e("code",[t._v("!")]),t._v(" 的时候,就意味着调用的是宏而不是普通函数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第三,"),s("code",[this._v('"Hello, world!"')]),this._v(" 是一个字符串。我们把这个字符串作为一个参数传递给 "),s("code",[this._v("println!")]),this._v(",字符串将被打印到屏幕上。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第四,该行以分号结尾("),s("code",[this._v(";")]),this._v("),这代表一个表达式的结束和下一个表达式的开始。大部分 Rust 代码行以分号结尾。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"编译和运行是彼此独立的步骤"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#编译和运行是彼此独立的步骤","aria-hidden":"true"}},[this._v("#")]),this._v(" 编译和运行是彼此独立的步骤")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在运行 Rust 程序之前,必须先使用 Rust 编辑器编译它,即输入 "),s("code",[this._v("rustc")]),this._v(" 命令并传入源文件名称,如下:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ rustc main.rs\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果你有 C 或 C++ 背景,就会发现这与 "),s("code",[this._v("gcc")]),this._v(" 和 "),s("code",[this._v("clang")]),this._v(" 类似。编译成功后,Rust 会输出一个二进制的可执行文件。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 Linux、macOS 或 Windows 的 PowerShell 上,在 shell 中输入 "),s("code",[this._v("ls")]),this._v(" 命令也可以看见这个可执行文件,如下:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("> ls\n\n\n Directory: Path:\\to\\the\\project\n\n\nMode LastWriteTime Length Name\n---- ------------- ------ ----\n-a---- 6/1/2018 7:31 AM 137728 main.exe\n-a---- 6/1/2018 7:31 AM 1454080 main.pdb\n-a---- 6/1/2018 7:31 AM 14 main.rs\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-cmd extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("> dir /B %= /B 参数表示只显示文件名 =%\nmain.exe\nmain.pdb\nmain.rs\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("这展示了扩展名为 "),e("em",[t._v(".rs")]),t._v(" 的源文件、可执行文件(在 Windows 下是 "),e("em",[t._v("main.exe")]),t._v(",其它平台是 "),e("em",[t._v("main")]),t._v("),以及当使用 CMD 时会有一个包含调试信息、扩展名为 "),e("em",[t._v(".pdb")]),t._v(" 的文件。从这里开始运行 "),e("em",[t._v("main")]),t._v(" 或 "),e("em",[t._v("main.exe")]),t._v(" 文件,如下:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ ./main # Windows 是 .\\main.exe\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果 "),s("em",[this._v("main.rs")]),this._v(" 是上文所述的 Hello, world! 程序,它将会在终端上打印 "),s("code",[this._v("Hello, world!")]),this._v("。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("如果你更熟悉动态语言,如 Ruby、Python 或 JavaScript,则可能不习惯将编译和运行分为两个单独的步骤。Rust 是一种 "),e("strong",[t._v("预编译静态类型")]),t._v("("),e("em",[t._v("ahead-of-time compiled")]),t._v(")语言,这意味着你可以编译程序,并将可执行文件送给其他人,他们甚至不需要安装 Rust 就可以运行。如果你给他人一个 "),e("em",[t._v(".rb")]),t._v("、"),e("em",[t._v(".py")]),t._v(" 或 "),e("em",[t._v(".js")]),t._v(" 文件,他们需要先分别安装 Ruby,Python,JavaScript 实现(运行时环境,VM)。不过在这些语言中,只需要一句命令就可以编译和运行程序。这一切都是语言设计上的权衡取舍。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("仅仅使用 "),s("code",[this._v("rustc")]),this._v(" 编译简单程序是没问题的,不过随着项目的增长,你可能需要管理你项目的方方面面,并让代码易于分享。接下来,我们要介绍一个叫做 Cargo 的工具,它会帮助你编写真实世界中的 Rust 程序。")])}],!1,null,null,null);a.options.__file="ch01-02-hello-world.md";s.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/17.36a5d5f3.js b/src/.vuepress/dist/assets/js/17.36a5d5f3.js
new file mode 100644
index 0000000..9ab02d0
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/17.36a5d5f3.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{270:function(t,e,s){"use strict";s.r(e);var r=s(0),a=Object(r.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch01-03-hello-cargo.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch01-03-hello-cargo.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),s("p",[t._v("最简单的 Rust 程序,比如我们刚刚编写的,没有任何依赖。所以如果使用 Cargo 来构建 Hello, world! 项目,将只会用到 Cargo 的构建代码那部分功能。如果编写更为复杂的 Rust 程序,你会添加依赖,这样如果你一开始就使用 Cargo 的话,添加依赖将会变得简单许多。")]),t._v(" "),s("p",[t._v("由于绝大多数 Rust 项目使用 Cargo,本书接下来的部分假设你也使用 Cargo。如果使用 “安装” 部分介绍的官方安装包的话,则自带了 Cargo。如果通过其他方式安装的话,可以在终端输入如下命令检查是否安装了 Cargo:")]),t._v(" "),t._m(2),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),s("p",[t._v("这个文件使用 "),s("a",{attrs:{href:"https://github.com/toml-lang/toml",target:"_blank",rel:"noopener noreferrer"}},[s("em",[t._v("TOML")]),s("OutboundLink")],1),t._v(" ("),s("em",[t._v("Tom's Obvious, Minimal Language")]),t._v(") 格式,这是 Cargo 配置文件的格式。")]),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._m(35),t._v(" "),s("p",[t._v("我们回顾下已学习的 Cargo 内容:")]),t._v(" "),t._m(36),t._v(" "),s("p",[t._v("使用 Cargo 的一个额外的优点是,不管你使用什么操作系统,其命令都是一样的。所以从现在开始本书将不再为 Linux 和 macOS 以及 Windows 提供相应的命令。")]),t._v(" "),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),s("p",[t._v("关于更多 Cargo 的信息,请查阅 "),s("a",{attrs:{href:"https://doc.rust-lang.org/cargo/",target:"_blank",rel:"noopener noreferrer"}},[t._v("其文档"),s("OutboundLink")],1),t._v("。")]),t._v(" "),t._m(43),t._v(" "),s("p",[t._v("你已经准备好开启 Rust 之旅了!在本章中,你学习了如何:")]),t._v(" "),t._m(44),t._v(" "),s("p",[t._v("是时候通过构建更实质性的程序来熟悉读写 Rust 代码了。所以在第二章我们会构建一个猜猜看游戏程序。如果你更愿意从学习 Rust 常用的编程概念开始,请阅读第三章,接着再回到第二章。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"hello-cargo"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#hello-cargo","aria-hidden":"true"}},[this._v("#")]),this._v(" Hello, Cargo!")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Cargo 是 Rust 的构建系统和包管理器。大多数 Rustacean 们使用 Cargo 来管理他们的 Rust 项目,因为它可以为你处理很多任务,比如构建代码、下载依赖库并编译这些库。(我们把代码所需要的库叫做 "),e("strong",[this._v("依赖")]),this._v("("),e("em",[this._v("dependencies")]),this._v("))。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo --version\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("如果你看到了版本号,说明已安装!如果看到类似 "),e("code",[this._v("command not found")]),this._v(" 的错误,你应该查看相应安装文档以确定如何单独安装 Cargo。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"使用-cargo-创建项目"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#使用-cargo-创建项目","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 Cargo 创建项目")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("我们使用 Cargo 创建一个新项目,然后看看与上面的 Hello, world! 项目有什么不同。回到 "),e("em",[this._v("projects")]),this._v(" 目录(或者你存放代码的目录)。接着,可在任何操作系统下运行以下命令:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo new hello_cargo\n$ cd hello_cargo\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("第一行命令新建了名为 "),e("em",[this._v("hello_cargo")]),this._v(" 的目录。我们将项目命名为 "),e("em",[this._v("hello_cargo")]),this._v(",同时 Cargo 在一个同名目录中创建项目文件。")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("进入 "),s("em",[t._v("hello_cargo")]),t._v(" 目录并列出文件。将会看到 Cargo 生成了两个文件和一个目录:一个 "),s("em",[t._v("Cargo.toml")]),t._v(" 文件,一个 "),s("em",[t._v("src")]),t._v(" 目录,以及位于 "),s("em",[t._v("src")]),t._v(" 目录中 "),s("em",[t._v("main.rs")]),t._v(" 文件。它也在 "),s("em",[t._v("hello_cargo")]),t._v(" 目录初始化了一个 git 仓库,以及一个 "),s("em",[t._v(".gitignore")]),t._v(" 文件。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("blockquote",[e("p",[this._v("注意:Git 是一个常用的版本控制系统(version control system, VCS)。可以通过 "),e("code",[this._v("--vcs")]),this._v(" 参数使 "),e("code",[this._v("cargo new")]),this._v(" 切换到其它版本控制系统(VCS),或者不使用 VCS。运行 "),e("code",[this._v("cargo new --help")]),this._v(" 参看可用的选项。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("请自行选用文本编辑器打开 "),e("em",[this._v("Cargo.toml")]),this._v(" 文件。它应该看起来如示例 1-2 所示:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-toml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[package]\nname = "hello_cargo"\nversion = "0.1.0"\nauthors = ["Your Name "]\nedition = "2018"\n\n[dependencies]\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 1-2: "),e("em",[this._v("cargo new")]),this._v(" 命令生成的 "),e("em",[this._v("Cargo.toml")]),this._v(" 的内容")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("第一行,"),e("code",[this._v("[package]")]),this._v(",是一个片段(section)标题,表明下面的语句用来配置一个包。随着我们在这个文件增加更多的信息,还将增加其他片段(section)。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("接下来的四行设置了 Cargo 编译程序所需的配置:项目的名称、版本和作者。Cargo 从环境中获取你的名字和 email 信息,所以如果这些信息不正确,请修改并保存此文件。附录 E 会介绍 "),e("code",[this._v("edition")]),this._v(" 的值。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("最后一行,"),e("code",[this._v("[dependencies]")]),this._v(",是罗列项目依赖的片段的开始。在 Rust 中,代码包被称为 "),e("em",[this._v("crates")]),this._v("。这个项目并不需要其他的 crate,不过在第二章的第一个项目会用到依赖,那时会用得上这个片段。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("现在打开 "),e("em",[this._v("src/main.rs")]),this._v(" 看看:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("main")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token function"}},[t._v("println!")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v('"Hello, world!"')]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Cargo 为你生成了一个 Hello World! 程序,正如我们之前编写的示例 1-1!目前为止,之前项目与 Cargo 生成项目的区别是 Cargo 将代码放在 "),e("em",[this._v("src")]),this._v(" 目录,同时项目根目录包含一个 "),e("em",[this._v("Cargo.toml")]),this._v(" 配置文件。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Cargo 期望源文件存放在 "),e("em",[this._v("src")]),this._v(" 目录中。项目根目录只存放 README、license 信息、配置文件和其他跟代码无关的文件。使用 Cargo 帮助你保持项目干净整洁,一切井井有条。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("如果没有用 Cargo 开始项目,比如我们创建的 Hello,world! 项目,可以将其转化为一个 Cargo 项目。将代码放入 "),e("em",[this._v("src")]),this._v(" 目录,并创建一个合适的 "),e("em",[this._v("Cargo.toml")]),this._v(" 文件。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"构建并运行-cargo-项目"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#构建并运行-cargo-项目","aria-hidden":"true"}},[this._v("#")]),this._v(" 构建并运行 Cargo 项目")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("现在让我们看看通过 Cargo 构建和运行 Hello, world! 程序有什么不同!在 "),e("em",[this._v("hello_cargo")]),this._v(" 目录下,输入下面的命令来构建项目:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo build\n Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)\n Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("这个命令会创建一个可执行文件 "),e("em",[this._v("target/debug/hello_cargo")]),this._v(" (在 Windows 上是 "),e("em",[this._v("target\\debug\\hello_cargo.exe")]),this._v("),而不是放在目前目录下。可以通过这个命令运行可执行文件:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ ./target/debug/hello_cargo # 或者在 Windows 下为 .\\target\\debug\\hello_cargo.exe\nHello, world!\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("如果一切顺利,终端上应该会打印出 "),e("code",[this._v("Hello, world!")]),this._v("。首次运行 "),e("code",[this._v("cargo build")]),this._v(" 时,也会使 Cargo 在项目根目录创建一个新文件:"),e("em",[this._v("Cargo.lock")]),this._v("。这个文件记录项目依赖的实际版本。这个项目并没有依赖,所以其内容比较少。你自己永远也不需要碰这个文件,让 Cargo 处理它就行了。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("我们刚刚使用 "),e("code",[this._v("cargo build")]),this._v(" 构建了项目,并使用 "),e("code",[this._v("./target/debug/hello_cargo")]),this._v(" 运行了程序,也可以使用 "),e("code",[this._v("cargo run")]),this._v(" 在一个命令中同时编译并运行生成的可执行文件:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo run\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/hello_cargo`\nHello, world!\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("注意这一次并没有出现表明 Cargo 正在编译 "),e("code",[this._v("hello_cargo")]),this._v(" 的输出。Cargo 发现文件并没有被改变,就直接运行了二进制文件。如果修改了源文件的话,Cargo 会在运行之前重新构建项目,并会出现像这样的输出:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo run\n Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)\n Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs\n Running `target/debug/hello_cargo`\nHello, world!\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Cargo 还提供了一个叫 "),e("code",[this._v("cargo check")]),this._v(" 的命令。该命令快速检查代码确保其可以编译,但并不产生可执行文件:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo check\n Checking hello_cargo v0.1.0 (file:///projects/hello_cargo)\n Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs\n")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("为什么你会不需要可执行文件呢?通常 "),s("code",[t._v("cargo check")]),t._v(" 要比 "),s("code",[t._v("cargo build")]),t._v(" 快得多,因为它省略了生成可执行文件的步骤。如果你在编写代码时持续的进行检查,"),s("code",[t._v("cargo check")]),t._v(" 会加速开发!为此很多 Rustaceans 编写代码时定期运行 "),s("code",[t._v("cargo check")]),t._v(" 确保它们可以编译。当准备好使用可执行文件时才运行 "),s("code",[t._v("cargo build")]),t._v("。")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("ul",[s("li",[t._v("可以使用 "),s("code",[t._v("cargo build")]),t._v(" 或 "),s("code",[t._v("cargo check")]),t._v(" 构建项目。")]),t._v(" "),s("li",[t._v("可以使用 "),s("code",[t._v("cargo run")]),t._v(" 一步构建并运行项目。")]),t._v(" "),s("li",[t._v("有别于将构建结果放在与源码相同的目录,Cargo 会将其放到 "),s("em",[t._v("target/debug")]),t._v(" 目录。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"发布(release)构建"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#发布(release)构建","aria-hidden":"true"}},[this._v("#")]),this._v(" 发布(release)构建")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("当项目最终准备好发布时,可以使用 "),s("code",[t._v("cargo build --release")]),t._v(" 来优化编译项目。这会在 "),s("em",[t._v("target/release")]),t._v(" 而不是 "),s("em",[t._v("target/debug")]),t._v(" 下生成可执行文件。这些优化可以让 Rust 代码运行的更快,不过启用这些优化也需要消耗更长的编译时间。这也就是为什么会有两种不同的配置:一种是为了开发,你需要经常快速重新构建;另一种是为用户构建最终程序,它们不会经常重新构建,并且希望程序运行得越快越好。如果你在测试代码的运行时间,请确保运行 "),s("code",[t._v("cargo build --release")]),t._v(" 并使用 "),s("em",[t._v("target/release")]),t._v(" 下的可执行文件进行测试。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"把-cargo-当作习惯"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#把-cargo-当作习惯","aria-hidden":"true"}},[this._v("#")]),this._v(" 把 Cargo 当作习惯")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("对于简单项目, Cargo 并不比 "),e("code",[this._v("rustc")]),this._v(" 提供了更多的优势,不过随着开发的深入,终将证明其价值。对于拥有多个 crate 的复杂项目,交给 Cargo 来协调构建将简单的多。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("即便 "),e("code",[this._v("hello_cargo")]),this._v(" 项目十分简单,它现在也使用了很多在你之后的 Rust 生涯将会用到的实用工具。其实,要在任何已存在的项目上工作时,可以使用如下命令通过 Git 检出代码,移动到该项目目录并构建:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ git clone someurl.com/someproject\n$ cd someproject\n$ cargo build\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"总结"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("ul",[s("li",[t._v("使用 "),s("code",[t._v("rustup")]),t._v(" 安装最新稳定版的 Rust")]),t._v(" "),s("li",[t._v("更新到新版的 Rust")]),t._v(" "),s("li",[t._v("打开本地安装的文档")]),t._v(" "),s("li",[t._v("直接通过 "),s("code",[t._v("rustc")]),t._v(" 编写并运行 Hello, world! 程序")]),t._v(" "),s("li",[t._v("使用 Cargo 创建并运行新项目")])])}],!1,null,null,null);a.options.__file="ch01-03-hello-cargo.md";e.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/18.82e00fba.js b/src/.vuepress/dist/assets/js/18.82e00fba.js
new file mode 100644
index 0000000..220bda9
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/18.82e00fba.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[18],{268:function(e,t,s){"use strict";s.r(t);var n=s(0),r=Object(n.a)({},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("div",{staticClass:"content"},[e._m(0),e._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch02-00-guessing-game-tutorial.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ch02-00-guessing-game-tutorial.md"),s("OutboundLink")],1),e._v(" "),s("br"),e._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),e._v(" "),e._m(1),e._v(" "),s("p",[e._v("我们会实现一个经典的新手编程问题:猜猜看游戏。它是这么工作的:程序将会随机生成一个 1 到 100 之间的随机整数。接着它会请玩家猜一个数并输入,然后提示猜测是大了还是小了。如果猜对了,它会打印祝贺信息并退出。")]),e._v(" "),e._m(2),e._v(" "),e._m(3),e._v(" "),e._m(4),e._m(5),e._v(" "),e._m(6),e._v(" "),e._m(7),e._v(" "),e._m(8),s("p",[e._v("如果 Cargo 从环境中获取的开发者信息不正确,修改这个文件并再次保存。")]),e._v(" "),e._m(9),e._v(" "),e._m(10),e._v(" "),e._m(11),e._m(12),e._v(" "),e._m(13),e._m(14),e._v(" "),e._m(15),e._v(" "),e._m(16),e._v(" "),e._m(17),e._v(" "),e._m(18),e._v(" "),e._m(19),e._m(20),e._v(" "),e._m(21),e._v(" "),e._m(22),s("p",[e._v("默认情况下,Rust 将 "),s("a",{attrs:{href:"https://doc.rust-lang.org/std/prelude/index.html",target:"_blank",rel:"noopener noreferrer"}},[s("em",[e._v("prelude")]),s("OutboundLink")],1),e._v(" 模块中少量的类型引入到每个程序的作用域中。如果需要的类型不在 prelude 中,你必须使用 "),s("code",[e._v("use")]),e._v(" 语句显式地将其引入作用域。"),s("code",[e._v("std::io")]),e._v(" 库提供很多有用的功能,包括接收用户输入的功能。")]),e._v(" "),e._m(23),e._v(" "),e._m(24),e._m(25),e._v(" "),e._m(26),e._v(" "),e._m(27),s("p",[e._v("这些代码仅仅打印提示,介绍游戏的内容然后请求用户输入。")]),e._v(" "),e._m(28),e._v(" "),s("p",[e._v("接下来,创建一个储存用户输入的地方,像这样:")]),e._v(" "),e._m(29),e._m(30),e._v(" "),e._m(31),e._m(32),e._v(" "),e._m(33),e._m(34),e._v(" "),s("p",[e._v("让我们回到猜猜看程序中。现在我们知道了 "),s("code",[e._v("let mut guess")]),e._v(" 会引入一个叫做 "),s("code",[e._v("guess")]),e._v(" 的可变变量。等号("),s("code",[e._v("=")]),e._v(")的右边是 "),s("code",[e._v("guess")]),e._v(" 所绑定的值,它是 "),s("code",[e._v("String::new")]),e._v(" 的结果,这个函数会返回一个 "),s("code",[e._v("String")]),e._v(" 的新实例。"),s("a",{attrs:{href:"https://doc.rust-lang.org/std/string/struct.String.html",target:"_blank",rel:"noopener noreferrer"}},[s("code",[e._v("String")]),s("OutboundLink")],1),e._v(" 是一个标准库提供的字符串类型,它是 UTF-8 编码的可增长文本块。")]),e._v(" "),e._m(35),e._v(" "),e._m(36),e._v(" "),e._m(37),e._v(" "),e._m(38),e._v(" "),e._m(39),s("p",[e._v("如果程序的开头没有 "),s("code",[e._v("use std::io")]),e._v(" 这一行,可以把函数调用写成 "),s("code",[e._v("std::io::stdin")]),e._v("。"),s("code",[e._v("stdin")]),e._v(" 函数返回一个 "),s("a",{attrs:{href:"https://doc.rust-lang.org/std/io/struct.Stdin.html",target:"_blank",rel:"noopener noreferrer"}},[s("code",[e._v("std::io::Stdin")]),s("OutboundLink")],1),e._v(" 的实例,这代表终端标准输入句柄的类型。")]),e._v(" "),s("p",[e._v("代码的下一部分,"),s("code",[e._v(".read_line(&mut guess)")]),e._v(",调用 "),s("a",{attrs:{href:"https://doc.rust-lang.org/std/io/struct.Stdin.html#method.read_line",target:"_blank",rel:"noopener noreferrer"}},[s("code",[e._v("read_line")]),s("OutboundLink")],1),e._v(" 方法从标准输入句柄获取用户输入。我们还向 "),s("code",[e._v("read_line()")]),e._v(" 传递了一个参数:"),s("code",[e._v("&mut guess")]),e._v("。")]),e._v(" "),e._m(40),e._v(" "),e._m(41),e._v(" "),e._m(42),e._v(" "),s("p",[e._v("我们还没有完全分析完这行代码。虽然这是单独一行代码,但它是一个逻辑行(虽然换行了但仍是一个语句)的第一部分。第二部分是这个方法:")]),e._v(" "),e._m(43),e._m(44),e._v(" "),e._m(45),s("p",[e._v("不过,过长的行难以阅读,所以最好拆开来写,两个方法调用占两行。现在来看看这行代码干了什么。")]),e._v(" "),s("p",[e._v("之前提到了 "),s("code",[e._v("read_line")]),e._v(" 将用户输入附加到传递给它的字符串中,不过它也返回一个值——在这个例子中是 "),s("a",{attrs:{href:"https://doc.rust-lang.org/std/io/type.Result.html",target:"_blank",rel:"noopener noreferrer"}},[s("code",[e._v("io::Result")]),s("OutboundLink")],1),e._v("。Rust 标准库中有很多叫做 "),s("code",[e._v("Result")]),e._v(" 的类型。一个 "),s("a",{attrs:{href:"https://doc.rust-lang.org/std/result/enum.Result.html",target:"_blank",rel:"noopener noreferrer"}},[s("code",[e._v("Result")]),s("OutboundLink")],1),e._v(" 泛型以及对应子模块的特定版本,比如 "),s("code",[e._v("io::Result")]),e._v("。")]),e._v(" "),s("p",[s("code",[e._v("Result")]),e._v(" 类型是 "),s("router-link",{attrs:{to:"./ch06-00-enums.html"}},[s("em",[e._v("枚举")]),e._v("("),s("em",[e._v("enumerations")]),e._v(")")]),e._v(",通常也写作 "),s("em",[e._v("enums")]),e._v("。枚举类型持有固定集合的值,这些值被称为枚举的 "),s("strong",[e._v("成员")]),e._v("("),s("em",[e._v("variants")]),e._v(")。第六章将介绍枚举的更多细节。")],1),e._v(" "),e._m(46),e._v(" "),s("p",[e._v("这些 "),s("code",[e._v("Result")]),e._v(" 类型的作用是编码错误处理信息。"),s("code",[e._v("Result")]),e._v(" 类型的值,像其他类型一样,拥有定义于其上的方法。"),s("code",[e._v("io::Result")]),e._v(" 的实例拥有 "),s("a",{attrs:{href:"https://doc.rust-lang.org/std/result/enum.Result.html#method.expect",target:"_blank",rel:"noopener noreferrer"}},[s("code",[e._v("expect")]),e._v(" 方法"),s("OutboundLink")],1),e._v("。如果 "),s("code",[e._v("io::Result")]),e._v(" 实例的值是 "),s("code",[e._v("Err")]),e._v(","),s("code",[e._v("expect")]),e._v(" 会导致程序崩溃,并显示当做参数传递给 "),s("code",[e._v("expect")]),e._v(" 的信息。如果 "),s("code",[e._v("read_line")]),e._v(" 方法返回 "),s("code",[e._v("Err")]),e._v(",则可能是来源于底层操作系统错误的结果。如果 "),s("code",[e._v("io::Result")]),e._v(" 实例的值是 "),s("code",[e._v("Ok")]),e._v(","),s("code",[e._v("expect")]),e._v(" 会获取 "),s("code",[e._v("Ok")]),e._v(" 中的值并原样返回。在本例中,这个值是用户输入到标准输入中的字节数。")]),e._v(" "),e._m(47),e._v(" "),e._m(48),e._m(49),e._v(" "),e._m(50),e._v(" "),e._m(51),e._v(" "),s("p",[e._v("除了位于结尾的大括号,目前为止就只有这一行代码值得讨论一下了,就是这一行:")]),e._v(" "),e._m(52),e._m(53),e._v(" "),e._m(54),e._m(55),e._v(" "),e._m(56),e._v(" "),e._m(57),e._v(" "),e._m(58),s("p",[e._v("至此为止,游戏的第一部分已经完成:我们从键盘获取输入并打印了出来。")]),e._v(" "),e._m(59),e._v(" "),s("p",[e._v("接下来,需要生成一个秘密数字,好让用户来猜。秘密数字应该每次都不同,这样重复玩才不会乏味;范围应该在 1 到 100 之间,这样才不会太困难。Rust 标准库中尚未包含随机数功能。然而,Rust 团队还是提供了一个 "),s("a",{attrs:{href:"https://crates.io/crates/rand",target:"_blank",rel:"noopener noreferrer"}},[s("code",[e._v("rand")]),e._v(" crate"),s("OutboundLink")],1),e._v("。")]),e._v(" "),e._m(60),e._v(" "),e._m(61),e._v(" "),e._m(62),e._v(" "),e._m(63),e._v(" "),e._m(64),s("p",[e._v("在 "),s("em",[e._v("Cargo.toml")]),e._v(" 文件中,标题以及之后的内容属同一个片段,直到遇到下一个标题才开始新的片段。"),s("code",[e._v("[dependencies]")]),e._v(" 片段告诉 Cargo 本项目依赖了哪些外部 crate 及其版本。本例中,我们使用语义化版本 "),s("code",[e._v("0.3.14")]),e._v(" 来指定 "),s("code",[e._v("rand")]),e._v(" crate。Cargo 理解"),s("a",{attrs:{href:"http://semver.org",target:"_blank",rel:"noopener noreferrer"}},[e._v("语义化版本(Semantic Versioning)"),s("OutboundLink")],1),e._v("(有时也称为 "),s("em",[e._v("SemVer")]),e._v("),这是一种定义版本号的标准。"),s("code",[e._v("0.3.14")]),e._v(" 事实上是 "),s("code",[e._v("^0.3.14")]),e._v(" 的简写,它表示 “任何与 0.3.14 版本公有 API 相兼容的版本”。")]),e._v(" "),s("p",[e._v("现在,不修改任何代码,构建项目,如示例 2-2 所示:")]),e._v(" "),e._m(65),e._m(66),e._v(" "),s("p",[e._v("可能会出现不同的版本号(多亏了语义化版本,它们与代码是兼容的!),同时显示顺序也可能会有所不同。")]),e._v(" "),s("p",[e._v("现在我们有了一个外部依赖,Cargo 从 "),s("em",[e._v("registry")]),e._v(" 上获取所有包的最新版本信息,这是一份来自 "),s("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[e._v("Crates.io"),s("OutboundLink")],1),e._v(" 的数据拷贝。Crates.io 是 Rust 生态环境中的开发者们向他人贡献 Rust 开源项目的地方。")]),e._v(" "),e._m(67),e._v(" "),e._m(68),e._v(" "),e._m(69),e._v(" "),e._m(70),e._m(71),e._v(" "),e._m(72),e._v(" "),e._m(73),e._v(" "),e._m(74),e._v(" "),e._m(75),e._v(" "),e._m(76),e._v(" "),e._m(77),e._v(" "),e._m(78),e._m(79),e._v(" "),e._m(80),e._v(" "),e._m(81),e._m(82),e._v(" "),s("p",[e._v("第十四章会讲到 "),s("a",{attrs:{href:"http://doc.crates.io",target:"_blank",rel:"noopener noreferrer"}},[e._v("Cargo"),s("OutboundLink")],1),e._v(" 及其"),s("a",{attrs:{href:"http://doc.crates.io/crates-io.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("生态系统"),s("OutboundLink")],1),e._v(" 的更多内容,不过目前你只需要了解这么多。通过 Cargo 复用库文件非常容易,因此 Rustacean 能够编写出由很多包组装而成的更轻巧的项目。")]),e._v(" "),e._m(83),e._v(" "),e._m(84),e._v(" "),e._m(85),e._v(" "),e._m(86),e._m(87),e._v(" "),e._m(88),e._v(" "),e._m(89),e._v(" "),e._m(90),e._v(" "),e._m(91),e._v(" "),s("p",[e._v("新增加的第二行代码打印出了秘密数字。这在开发程序时很有用,因为可以测试它,不过在最终版本中会删掉它。如果游戏一开始就打印出结果就没什么可玩的了!")]),e._v(" "),s("p",[e._v("尝试运行程序几次:")]),e._v(" "),e._m(92),s("p",[e._v("你应该能得到不同的随机数,同时它们应该都是在 1 和 100 之间的。干得漂亮!")]),e._v(" "),e._m(93),e._v(" "),s("p",[e._v("现在有了用户输入和一个随机数,我们可以比较它们。这个步骤如示例 2-4 所示。注意这段代码还不能通过编译,我们稍后会解释。")]),e._v(" "),e._m(94),e._v(" "),e._m(95),e._m(96),e._v(" "),e._m(97),e._v(" "),s("p",[e._v("接着,底部的五行新代码使用了 "),s("code",[e._v("Ordering")]),e._v(" 类型,"),s("code",[e._v("cmp")]),e._v(" 方法用来比较两个值并可以在任何可比较的值上调用。它获取一个被比较值的引用:这里是把 "),s("code",[e._v("guess")]),e._v(" 与 "),s("code",[e._v("secret_number")]),e._v(" 做比较。 然后它会返回一个刚才通过 "),s("code",[e._v("use")]),e._v(" 引入作用域的 "),s("code",[e._v("Ordering")]),e._v(" 枚举的成员。使用一个 "),s("router-link",{attrs:{to:"./ch06-02-match.html"}},[s("code",[e._v("match")])]),e._v(" 表达式,根据对 "),s("code",[e._v("guess")]),e._v(" 和 "),s("code",[e._v("secret_number")]),e._v(" 调用 "),s("code",[e._v("cmp")]),e._v(" 返回的 "),s("code",[e._v("Ordering")]),e._v(" 成员来决定接下来做什么。")],1),e._v(" "),e._m(98),e._v(" "),e._m(99),e._v(" "),s("p",[e._v("然而,示例 2-4 的代码并不能编译,可以尝试一下:")]),e._v(" "),e._m(100),e._m(101),e._v(" "),e._m(102),e._v(" "),e._m(103),e._v(" "),e._m(104),s("p",[e._v("这两行新代码是:")]),e._v(" "),e._m(105),e._m(106),e._v(" "),e._m(107),e._v(" "),s("p",[s("a",{attrs:{href:"https://doc.rust-lang.org/std/primitive.str.html#method.parse",target:"_blank",rel:"noopener noreferrer"}},[e._v("字符串的 "),s("code",[e._v("parse")]),e._v(" 方法"),s("OutboundLink")],1),e._v(" 将字符串解析成数字。因为这个方法可以解析多种数字类型,因此需要告诉 Rust 具体的数字类型,这里通过 "),s("code",[e._v("let guess: u32")]),e._v(" 指定。"),s("code",[e._v("guess")]),e._v(" 后面的冒号("),s("code",[e._v(":")]),e._v(")告诉 Rust 我们指定了变量的类型。Rust 有一些内建的数字类型;"),s("code",[e._v("u32")]),e._v(" 是一个无符号的 32 位整型。对于不大的正整数来说,它是不错的类型,第三章还会讲到其他数字类型。另外,程序中的 "),s("code",[e._v("u32")]),e._v(" 注解以及与 "),s("code",[e._v("secret_number")]),e._v(" 的比较,意味着 Rust 会推断出 "),s("code",[e._v("secret_number")]),e._v(" 也是 "),s("code",[e._v("u32")]),e._v(" 类型。现在可以使用相同类型比较两个值了!")]),e._v(" "),e._m(108),e._v(" "),s("p",[e._v("现在让我们运行程序!")]),e._v(" "),e._m(109),s("p",[e._v("漂亮!即便是在猜测之前添加了空格,程序依然能判断出用户猜测了 76。多运行程序几次,输入不同的数字来检验不同的行为:猜一个正确的数字,猜一个过大的数字和猜一个过小的数字。")]),e._v(" "),s("p",[e._v("现在游戏已经大体上能玩了,不过用户只能猜一次。增加一个循环来改变它吧!")]),e._v(" "),e._m(110),e._v(" "),e._m(111),e._v(" "),e._m(112),e._v(" "),e._m(113),s("p",[e._v("如上所示,我们将提示用户猜测之后的所有内容放入了循环。确保 loop 循环中的代码多缩进四个空格,再次运行程序。注意这里有一个新问题,因为程序忠实地执行了我们的要求:永远地请求另一个猜测,用户好像无法退出啊!")]),e._v(" "),e._m(114),e._v(" "),e._m(115),e._m(116),e._v(" "),e._m(117),e._v(" "),e._m(118),e._v(" "),e._m(119),e._v(" "),e._m(120),e._m(121),e._v(" "),e._m(122),e._v(" "),e._m(123),e._v(" "),e._m(124),e._v(" "),e._m(125),e._m(126),e._v(" "),e._m(127),e._v(" "),e._m(128),e._v(" "),e._m(129),e._v(" "),e._m(130),e._v(" "),e._m(131),e._m(132),e._v(" "),e._m(133),e._v(" "),e._m(134),e._m(135),e._v(" "),e._m(136),e._v(" "),s("p",[e._v("此时此刻,你顺利完成了猜猜看游戏。恭喜!")]),e._v(" "),e._m(137)])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"编写-猜猜看-游戏"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#编写-猜猜看-游戏","aria-hidden":"true"}},[this._v("#")]),this._v(" 编写 猜猜看 游戏")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("让我们一起动手完成一个项目,来快速上手 Rust!本章将介绍 Rust 中一些常用概念,并通过真实的程序来展示如何运用它们。你将会学到 "),t("code",[this._v("let")]),this._v("、"),t("code",[this._v("match")]),this._v("、方法、关联函数、外部 crate 等知识!后续章节会深入探讨这些概念的细节。在这一章,我们将做基础练习。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"准备一个新项目"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#准备一个新项目","aria-hidden":"true"}},[this._v("#")]),this._v(" 准备一个新项目")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("要创建一个新项目,进入第一章中创建的 "),t("em",[this._v("projects")]),this._v(" 目录,使用 Cargo 新建一个项目,如下:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo new guessing_game\n$ cd guessing_game\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("第一个命令,"),t("code",[this._v("cargo new")]),this._v(",它获取项目的名称("),t("code",[this._v("guessing_game")]),this._v(")作为第一个参数。第二个命令进入到新创建的项目目录。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("看看生成的 "),t("em",[this._v("Cargo.toml")]),this._v(" 文件:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-toml extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('[package]\nname = "guessing_game"\nversion = "0.1.0"\nauthors = ["Your Name "]\n\n[dependencies]\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("正如第一章那样,"),t("code",[this._v("cargo new")]),this._v(" 生成了一个 “Hello, world!” 程序。查看 "),t("em",[this._v("src/main.rs")]),this._v(" 文件:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[e._v("fn")]),e._v(" "),s("span",{attrs:{class:"token function"}},[e._v("main")]),s("span",{attrs:{class:"token punctuation"}},[e._v("(")]),s("span",{attrs:{class:"token punctuation"}},[e._v(")")]),e._v(" "),s("span",{attrs:{class:"token punctuation"}},[e._v("{")]),e._v("\n "),s("span",{attrs:{class:"token function"}},[e._v("println!")]),s("span",{attrs:{class:"token punctuation"}},[e._v("(")]),s("span",{attrs:{class:"token string"}},[e._v('"Hello, world!"')]),s("span",{attrs:{class:"token punctuation"}},[e._v(")")]),s("span",{attrs:{class:"token punctuation"}},[e._v(";")]),e._v("\n"),s("span",{attrs:{class:"token punctuation"}},[e._v("}")]),e._v("\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("现在使用 "),t("code",[this._v("cargo run")]),this._v(" 命令,一步完成 “Hello, world!” 程序的编译和运行:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo run\n Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\n Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs\n Running `target/debug/guessing_game`\nHello, world!\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("当你需要在项目中快速迭代时,"),t("code",[this._v("run")]),this._v(" 命令就能派上用场,正如我们在这个游戏项目中做的,在下一次迭代之前快速测试每一次迭代。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("重新打开 "),t("em",[this._v("src/main.rs")]),this._v(" 文件。我们将会在这个文件中编写全部的代码。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"处理一次猜测"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#处理一次猜测","aria-hidden":"true"}},[this._v("#")]),this._v(" 处理一次猜测")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("猜猜看程序的第一部分请求和处理用户输入,并检查输入是否符合预期的格式。首先,允许玩家输入猜测。在 "),t("em",[this._v("src/main.rs")]),this._v(" 中输入示例 2-1 中的代码。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('use std::io;\n\nfn main() {\n println!("Guess the number!");\n\n println!("Please input your guess.");\n\n let mut guess = String::new();\n\n io::stdin().read_line(&mut guess)\n .expect("Failed to read line");\n\n println!("You guessed: {}", guess);\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 2-1:获取用户猜测并打印的代码")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("这些代码包含很多信息,我们一行一行地过一遍。为了获取用户输入并打印结果作为输出,我们需要将 "),t("code",[this._v("io")]),this._v("(输入/输出)库引入当前作用域。"),t("code",[this._v("io")]),this._v(" 库来自于标准库(也被称为 "),t("code",[this._v("std")]),this._v("):")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("use std::io;\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("如第一章所提及,"),t("code",[this._v("main")]),this._v(" 函数是程序的入口点:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("fn main() {\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("code",[this._v("fn")]),this._v(" 语法声明了一个新函数,"),t("code",[this._v("()")]),this._v(" 表明没有参数,"),t("code",[this._v("{")]),this._v(" 作为函数体的开始。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("第一章也提及了 "),t("code",[this._v("println!")]),this._v(" 是一个在屏幕上打印字符串的宏:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('println!("Guess the number!");\n\nprintln!("Please input your guess.");\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"使用变量储存值"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#使用变量储存值","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用变量储存值")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("let mut guess = String::new();\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("现在程序开始变得有意思了!这一小行代码发生了很多事。注意这是一个 "),t("code",[this._v("let")]),this._v(" 语句,用来创建 "),t("strong",[this._v("变量")]),this._v("("),t("em",[this._v("variable")]),this._v(")。这里是另外一个例子:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("let foo = bar;\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("这行代码新建了一个叫做 "),t("code",[this._v("foo")]),this._v(" 的变量并把它绑定到值 "),t("code",[this._v("bar")]),this._v(" 上。在 Rust 中,变量默认是不可变的。我们将会在第三章的 “变量与可变性” 部分详细讨论这个概念。下面的例子展示了如何在变量名前使用 "),t("code",[this._v("mut")]),this._v(" 来使一个变量可变:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("let foo = 5; // 不可变\nlet mut bar = 5; // 可变\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("blockquote",[t("p",[this._v("注意:"),t("code",[this._v("//")]),this._v(" 语法开始一个注释,持续到行尾。Rust 忽略注释中的所有内容,第三章将会详细介绍注释。")])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[s("code",[e._v("::new")]),e._v(" 那一行的 "),s("code",[e._v("::")]),e._v(" 语法表明 "),s("code",[e._v("new")]),e._v(" 是 "),s("code",[e._v("String")]),e._v(" 类型的一个 "),s("strong",[e._v("关联函数")]),e._v("("),s("em",[e._v("associated function")]),e._v(")。关联函数是针对类型实现的,在这个例子中是 "),s("code",[e._v("String")]),e._v(",而不是 "),s("code",[e._v("String")]),e._v(" 的某个特定实例。一些语言中把它称为 "),s("strong",[e._v("静态方法")]),e._v("("),s("em",[e._v("static method")]),e._v(")。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("code",[this._v("new")]),this._v(" 函数创建了一个新的空字符串,你会发现很多类型上有 "),t("code",[this._v("new")]),this._v(" 函数,因为它是创建类型实例的惯用函数名。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("总结一下,"),t("code",[this._v("let mut guess = String::new();")]),this._v(" 这一行创建了一个可变变量,当前它绑定到一个新的 "),t("code",[this._v("String")]),this._v(" 空实例上。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("回忆一下,我们在程序的第一行使用 "),t("code",[this._v("use std::io;")]),this._v(" 从标准库中引入了输入/输出功能。现在调用 "),t("code",[this._v("io")]),this._v(" 的关联函数 "),t("code",[this._v("stdin")]),this._v(":")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('io::stdin().read_line(&mut guess)\n .expect("Failed to read line");\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("code",[this._v("read_line")]),this._v(" 的工作是,无论用户在标准输入中键入什么内容,都将其存入一个字符串中,因此它需要字符串作为参数。这个字符串参数应该是可变的,以便 "),t("code",[this._v("read_line")]),this._v(" 将用户输入附加上去。")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[s("code",[e._v("&")]),e._v(" 表示这个参数是一个 "),s("strong",[e._v("引用")]),e._v("("),s("em",[e._v("reference")]),e._v("),它允许多处代码访问同一处数据,而无需在内存中多次拷贝。引用是一个复杂的特性,Rust 的一个主要优势就是安全而简单的操纵引用。完成当前程序并不需要了解如此多细节。现在,我们只需知道它像变量一样,默认是不可变的。因此,需要写成 "),s("code",[e._v("&mut guess")]),e._v(" 来使其可变,而不是 "),s("code",[e._v("&guess")]),e._v("。(第四章会更全面的解释引用。)")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"使用-result-类型来处理潜在的错误"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#使用-result-类型来处理潜在的错误","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),t("code",[this._v("Result")]),this._v(" 类型来处理潜在的错误")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('.expect("Failed to read line");\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("当使用 "),t("code",[this._v(".foo()")]),this._v(" 语法调用方法时,通过换行加缩进来把长行拆开是明智的。我们完全可以这样写:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('io::stdin().read_line(&mut guess).expect("Failed to read line");\n')])])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[s("code",[e._v("Result")]),e._v(" 的成员是 "),s("code",[e._v("Ok")]),e._v(" 和 "),s("code",[e._v("Err")]),e._v(","),s("code",[e._v("Ok")]),e._v(" 成员表示操作成功,内部包含成功时产生的值。"),s("code",[e._v("Err")]),e._v(" 成员则意味着操作失败,并且包含失败的前因后果。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("如果不调用 "),t("code",[this._v("expect")]),this._v(",程序也能编译,不过会出现一个警告:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo build\n Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\nwarning: unused `std::result::Result` which must be used\n --\x3e src/main.rs:10:5\n |\n10 | io::stdin().read_line(&mut guess);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = note: #[warn(unused_must_use)] on by default\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("Rust 警告我们没有使用 "),t("code",[this._v("read_line")]),this._v(" 的返回值 "),t("code",[this._v("Result")]),this._v(",说明有一个可能的错误没有处理。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("消除警告的正确做法是实际编写错误处理代码,不过由于我们就是希望程序在出现问题时立即崩溃,所以直接使用 "),t("code",[this._v("expect")]),this._v("。第九章会学习如何从错误中恢复。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"使用-println-占位符打印值"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#使用-println-占位符打印值","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),t("code",[this._v("println!")]),this._v(" 占位符打印值")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('println!("You guessed: {}", guess);\n')])])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("这行代码打印存储用户输入的字符串。第一个参数是格式化字符串,里面的 "),s("code",[e._v("{}")]),e._v(" 是预留在特定位置的占位符。使用 "),s("code",[e._v("{}")]),e._v(" 也可以打印多个值:第一对 "),s("code",[e._v("{}")]),e._v(" 使用格式化字符串之后的第一个值,第二对则使用第二个值,依此类推。调用一次 "),s("code",[e._v("println!")]),e._v(" 打印多个值看起来像这样:")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[e._v("let")]),e._v(" x "),s("span",{attrs:{class:"token operator"}},[e._v("=")]),e._v(" "),s("span",{attrs:{class:"token number"}},[e._v("5")]),s("span",{attrs:{class:"token punctuation"}},[e._v(";")]),e._v("\n"),s("span",{attrs:{class:"token keyword"}},[e._v("let")]),e._v(" y "),s("span",{attrs:{class:"token operator"}},[e._v("=")]),e._v(" "),s("span",{attrs:{class:"token number"}},[e._v("10")]),s("span",{attrs:{class:"token punctuation"}},[e._v(";")]),e._v("\n\n"),s("span",{attrs:{class:"token function"}},[e._v("println!")]),s("span",{attrs:{class:"token punctuation"}},[e._v("(")]),s("span",{attrs:{class:"token string"}},[e._v('"x = {} and y = {}"')]),s("span",{attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" x"),s("span",{attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" y"),s("span",{attrs:{class:"token punctuation"}},[e._v(")")]),s("span",{attrs:{class:"token punctuation"}},[e._v(";")]),e._v("\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("这行代码会打印出 "),t("code",[this._v("x = 5 and y = 10")]),this._v("。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"测试第一部分代码"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#测试第一部分代码","aria-hidden":"true"}},[this._v("#")]),this._v(" 测试第一部分代码")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("让我们来测试下猜猜看游戏的第一部分。使用 "),t("code",[this._v("cargo run")]),this._v(" 运行:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo run\n Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\n Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs\n Running `target/debug/guessing_game`\nGuess the number!\nPlease input your guess.\n6\nYou guessed: 6\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"生成一个秘密数字"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#生成一个秘密数字","aria-hidden":"true"}},[this._v("#")]),this._v(" 生成一个秘密数字")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"使用-crate-来增加更多功能"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#使用-crate-来增加更多功能","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 crate 来增加更多功能")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("记住,"),s("em",[e._v("crate")]),e._v(" 是一个 Rust 代码包。我们正在构建的项目是一个 "),s("strong",[e._v("二进制 crate")]),e._v(",它生成一个可执行文件。 "),s("code",[e._v("rand")]),e._v(" crate 是一个 "),s("strong",[e._v("库 crate")]),e._v(",库 crate 可以包含任意能被其他程序使用的代码。")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("Cargo 对外部 crate 的运用是其真正闪光的地方。在我们使用 "),s("code",[e._v("rand")]),e._v(" 编写代码之前,需要修改 "),s("em",[e._v("Cargo.toml")]),e._v(" 文件,引入一个 "),s("code",[e._v("rand")]),e._v(" 依赖。现在打开这个文件并在底部的 "),s("code",[e._v("[dependencies]")]),e._v(" 片段标题之下添加:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-toml extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('[dependencies]\n\nrand = "0.3.14"\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo build\n Updating registry `https://github.com/rust-lang/crates.io-index`\n Downloading rand v0.3.14\n Downloading libc v0.2.14\n Compiling libc v0.2.14\n Compiling rand v0.3.14\n Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\n Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 2-2: 将 rand crate 添加为依赖之后运行 "),t("code",[this._v("cargo build")]),this._v(" 的输出")])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("在更新完 registry 后,Cargo 检查 "),s("code",[e._v("[dependencies]")]),e._v(" 片段并下载缺失的 crate 。本例中,虽然只声明了 "),s("code",[e._v("rand")]),e._v(" 一个依赖,然而 Cargo 还是额外获取了 "),s("code",[e._v("libc")]),e._v(" 的拷贝,因为 "),s("code",[e._v("rand")]),e._v(" 依赖 "),s("code",[e._v("libc")]),e._v(" 来正常工作。下载完成后,Rust 编译依赖,然后使用这些依赖编译项目。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("如果不做任何修改,立刻再次运行 "),t("code",[this._v("cargo build")]),this._v(",则不会看到任何除了 "),t("code",[this._v("Finished")]),this._v(" 行之外的输出。Cargo 知道它已经下载并编译了依赖,同时 "),t("em",[this._v("Cargo.toml")]),this._v(" 文件也没有变动。Cargo 还知道代码也没有任何修改,所以它不会重新编译代码。因为无事可做,它简单的退出了。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("如果打开 "),t("em",[this._v("src/main.rs")]),this._v(" 文件,做一些无关紧要的修改,保存并再次构建,则会出现两行输出:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo build\n Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\n Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("这一行表示 Cargo 只针对 "),t("em",[this._v("src/main.rs")]),this._v(" 文件的微小修改而更新构建。依赖没有变化,所以 Cargo 知道它可以复用已经为此下载并编译的代码。它只是重新构建了部分(项目)代码。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h4",{attrs:{id:"cargo-lock-文件确保构建是可重现的"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#cargo-lock-文件确保构建是可重现的","aria-hidden":"true"}},[this._v("#")]),this._v(" "),t("em",[this._v("Cargo.lock")]),this._v(" 文件确保构建是可重现的")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("Cargo 有一个机制来确保任何人在任何时候重新构建代码,都会产生相同的结果:Cargo 只会使用你指定的依赖版本,除非你又手动指定了别的。例如,如果下周 "),t("code",[this._v("rand")]),this._v(" crate 的 "),t("code",[this._v("0.3.15")]),this._v(" 版本出来了,它修复了一个重要的 bug,同时也含有一个会破坏代码运行的缺陷,这时会发生什么呢?")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("这个问题的答案是 "),s("em",[e._v("Cargo.lock")]),e._v(" 文件。它在第一次运行 "),s("code",[e._v("cargo build")]),e._v(" 时创建,并放在 "),s("em",[e._v("guessing_game")]),e._v(" 目录。当第一次构建项目时,Cargo 计算出所有符合要求的依赖版本并写入 "),s("em",[e._v("Cargo.lock")]),e._v(" 文件。当将来构建项目时,Cargo 会发现 "),s("em",[e._v("Cargo.lock")]),e._v(" 已存在并使用其中指定的版本,而不是再次计算所有的版本。这使得你拥有了一个自动化的可重现的构建。换句话说,项目会持续使用 "),s("code",[e._v("0.3.14")]),e._v(" 直到你显式升级,多亏有了 "),s("em",[e._v("Cargo.lock")]),e._v(" 文件。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h4",{attrs:{id:"更新-crate-到一个新版本"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#更新-crate-到一个新版本","aria-hidden":"true"}},[this._v("#")]),this._v(" 更新 crate 到一个新版本")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("当你 "),s("strong",[e._v("确实")]),e._v(" 需要升级 crate 时,Cargo 提供了另一个命令,"),s("code",[e._v("update")]),e._v(",它会忽略 "),s("em",[e._v("Cargo.lock")]),e._v(" 文件,并计算出所有符合 "),s("em",[e._v("Cargo.toml")]),e._v(" 声明的最新版本。如果成功了,Cargo 会把这些版本写入 "),s("em",[e._v("Cargo.lock")]),e._v(" 文件。")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("不过,Cargo 默认只会寻找大于 "),s("code",[e._v("0.3.0")]),e._v(" 而小于 "),s("code",[e._v("0.4.0")]),e._v(" 的版本。如果 "),s("code",[e._v("rand")]),e._v(" crate 发布了两个新版本,"),s("code",[e._v("0.3.15")]),e._v(" 和 "),s("code",[e._v("0.4.0")]),e._v(",在运行 "),s("code",[e._v("cargo update")]),e._v(" 时会出现如下内容:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo update\n Updating registry `https://github.com/rust-lang/crates.io-index`\n Updating rand v0.3.14 -> v0.3.15\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("这时,你也会注意到的 "),t("em",[this._v("Cargo.lock")]),this._v(" 文件中的变化无外乎现在使用的 "),t("code",[this._v("rand")]),this._v(" crate 版本是"),t("code",[this._v("0.3.15")])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("如果想要使用 "),s("code",[e._v("0.4.0")]),e._v(" 版本的 "),s("code",[e._v("rand")]),e._v(" 或是任何 "),s("code",[e._v("0.4.x")]),e._v(" 系列的版本,必须像这样更新 "),s("em",[e._v("Cargo.toml")]),e._v(" 文件:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-toml extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('[dependencies]\n\nrand = "0.4.0"\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("下一次运行 "),t("code",[this._v("cargo build")]),this._v(" 时,Cargo 会从 registry 更新可用的 crate,并根据你指定的新版本重新计算。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"生成一个随机数"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#生成一个随机数","aria-hidden":"true"}},[this._v("#")]),this._v(" 生成一个随机数")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("你已经把 "),s("code",[e._v("rand")]),e._v(" crate 添加到 "),s("em",[e._v("Cargo.toml")]),e._v(" 了,让我们开始 "),s("strong",[e._v("使用")]),e._v(" "),s("code",[e._v("rand")]),e._v(" 吧。下一步是更新 "),s("em",[e._v("src/main.rs")]),e._v(",如示例 2-3 所示。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('extern crate rand;\n\nuse std::io;\nuse rand::Rng;\n\nfn main() {\n println!("Guess the number!");\n\n let secret_number = rand::thread_rng().gen_range(1, 101);\n\n println!("The secret number is: {}", secret_number);\n\n println!("Please input your guess.");\n\n let mut guess = String::new();\n\n io::stdin().read_line(&mut guess)\n .expect("Failed to read line");\n\n println!("You guessed: {}", guess);\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 2-3:添加生成随机数的代码")])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("首先,这里新增了一行通知 Rust 我们要使用外部依赖 "),s("code",[e._v("rand")]),e._v("。这等同于调用 "),s("code",[e._v("use rand")]),e._v(",所以现在可以使用 "),s("code",[e._v("rand::")]),e._v(" 前缀来调用 "),s("code",[e._v("rand")]),e._v(" crate 中的任何内容。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("接下来增加了另一行 "),t("code",[this._v("use")]),this._v(":"),t("code",[this._v("use rand::Rng")]),this._v("。"),t("code",[this._v("Rng")]),this._v(" 是一个 trait,它定义了随机数生成器应实现的方法,想使用这些方法的话,此 trait 必须在作用域中。第十章会详细介绍 trait。")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("另外,中间还新增加了两行。"),s("code",[e._v("rand::thread_rng")]),e._v(" 函数提供实际使用的随机数生成器:它位于当前执行线程的本地环境中,并从操作系统获取 seed。接下来,调用随机数生成器的 "),s("code",[e._v("gen_range")]),e._v(" 方法。这个方法由刚才引入到作用域的 "),s("code",[e._v("Rng")]),e._v(" trait 定义。"),s("code",[e._v("gen_range")]),e._v(" 方法获取两个数字作为参数,并生成一个范围在两者之间的随机数。它包含下限但不包含上限,所以需要指定 "),s("code",[e._v("1")]),e._v(" 和 "),s("code",[e._v("101")]),e._v(" 来请求一个 1 和 100 之间的数。")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("blockquote",[s("p",[e._v("注意:你不可能凭空就知道应该 use 哪个 trait 以及该从 crate 中调用哪个方法。crate 的使用说明位于其文档中。Cargo 有一个很棒的功能是:运行 "),s("code",[e._v("cargo doc --open")]),e._v(" 命令来构建所有本地依赖提供的文档,并在浏览器中打开。例如,假设你对 "),s("code",[e._v("rand")]),e._v(" crate 中的其他功能感兴趣,你可以运行 "),s("code",[e._v("cargo doc --open")]),e._v(" 并点击左侧导航栏中的 "),s("code",[e._v("rand")]),e._v("。")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo run\n Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\n Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs\n Running `target/debug/guessing_game`\nGuess the number!\nThe secret number is: 7\nPlease input your guess.\n4\nYou guessed: 4\n$ cargo run\n Running `target/debug/guessing_game`\nGuess the number!\nThe secret number is: 83\nPlease input your guess.\n5\nYou guessed: 5\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"比较猜测的数字和秘密数字"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#比较猜测的数字和秘密数字","aria-hidden":"true"}},[this._v("#")]),this._v(" 比较猜测的数字和秘密数字")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('use std::io;\nuse std::cmp::Ordering;\nuse rand::Rng;\n\nfn main() {\n\n // ---snip---\n\n println!("You guessed: {}", guess);\n\n match guess.cmp(&secret_number) {\n Ordering::Less => println!("Too small!"),\n Ordering::Greater => println!("Too big!"),\n Ordering::Equal => println!("You win!"),\n }\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 2-4:处理比较两个数字可能的返回值")])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("新代码的第一行是另一个 "),s("code",[e._v("use")]),e._v(",从标准库引入了一个叫做 "),s("code",[e._v("std::cmp::Ordering")]),e._v(" 的类型。同 "),s("code",[e._v("Result")]),e._v(" 一样, "),s("code",[e._v("Ordering")]),e._v(" 也是一个枚举,不过它的成员是 "),s("code",[e._v("Less")]),e._v("、"),s("code",[e._v("Greater")]),e._v(" 和 "),s("code",[e._v("Equal")]),e._v("。这是比较两个值时可能出现的三种结果。")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("一个 "),s("code",[e._v("match")]),e._v(" 表达式由 "),s("strong",[e._v("分支(arms)")]),e._v(" 构成。一个分支包含一个 "),s("strong",[e._v("模式")]),e._v("("),s("em",[e._v("pattern")]),e._v(")和表达式开头的值与分支模式相匹配时应该执行的代码。Rust 获取提供给 "),s("code",[e._v("match")]),e._v(" 的值并挨个检查每个分支的模式。"),s("code",[e._v("match")]),e._v(" 结构和模式是 Rust 中强大的功能,它体现了代码可能遇到的多种情形,并帮助你确保没有遗漏处理。这些功能将分别在第六章和第十八章详细介绍。")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("让我们看看使用 "),s("code",[e._v("match")]),e._v(" 表达式的例子。假设用户猜了 50,这时随机生成的秘密数字是 38。比较 50 与 38 时,因为 50 比 38 要大,"),s("code",[e._v("cmp")]),e._v(" 方法会返回 "),s("code",[e._v("Ordering::Greater")]),e._v("。"),s("code",[e._v("Ordering::Greater")]),e._v(" 是 "),s("code",[e._v("match")]),e._v(" 表达式得到的值。它检查第一个分支的模式,"),s("code",[e._v("Ordering::Less")]),e._v(" 与 "),s("code",[e._v("Ordering::Greater")]),e._v("并不匹配,所以它忽略了这个分支的代码并来到下一个分支。下一个分支的模式是 "),s("code",[e._v("Ordering::Greater")]),e._v(","),s("strong",[e._v("正确")]),e._v(" 匹配!这个分支关联的代码被执行,在屏幕打印出 "),s("code",[e._v("Too big!")]),e._v("。"),s("code",[e._v("match")]),e._v(" 表达式就此终止,因为该场景下没有检查最后一个分支的必要。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo build\n Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\nerror[E0308]: mismatched types\n --\x3e src/main.rs:23:21\n |\n23 | match guess.cmp(&secret_number) {\n | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integral variable\n |\n = note: expected type `&std::string::String`\n = note: found type `&{integer}`\n\nerror: aborting due to previous error\nCould not compile `guessing_game`.\n")])])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("错误的核心表明这里有 "),s("strong",[e._v("不匹配的类型")]),e._v("("),s("em",[e._v("mismatched types")]),e._v(")。Rust 有一个静态强类型系统,同时也有类型推断。当我们写出 "),s("code",[e._v("let guess = String::new()")]),e._v(" 时,Rust 推断出 "),s("code",[e._v("guess")]),e._v(" 应该是 "),s("code",[e._v("String")]),e._v(" 类型,并不需要我们写出类型。另一方面,"),s("code",[e._v("secret_number")]),e._v(",是数字类型。几个数字类型拥有 1 到 100 之间的值:32 位数字 "),s("code",[e._v("i32")]),e._v(";32 位无符号数字 "),s("code",[e._v("u32")]),e._v(";64 位数字 "),s("code",[e._v("i64")]),e._v(" 等等。Rust 默认使用 "),s("code",[e._v("i32")]),e._v(",所以它是 "),s("code",[e._v("secret_number")]),e._v(" 的类型,除非增加类型信息,或任何能让 Rust 推断出不同数值类型的信息。这里错误的原因在于 Rust 不会比较字符串类型和数字类型。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("所以我们必须把从输入中读取到的 "),t("code",[this._v("String")]),this._v(" 转换为一个真正的数字类型,才好与秘密数字进行比较。这可以通过在 "),t("code",[this._v("main")]),this._v(" 函数体中增加如下两行代码来实现:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('// --snip--\n\n let mut guess = String::new();\n\n io::stdin().read_line(&mut guess)\n .expect("Failed to read line");\n\n let guess: u32 = guess.trim().parse()\n .expect("Please type a number!");\n\n println!("You guessed: {}", guess);\n\n match guess.cmp(&secret_number) {\n Ordering::Less => println!("Too small!"),\n Ordering::Greater => println!("Too big!"),\n Ordering::Equal => println!("You win!"),\n }\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('let guess: u32 = guess.trim().parse()\n .expect("Please type a number!");\n')])])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("这里创建了一个叫做 "),s("code",[e._v("guess")]),e._v(" 的变量。不过等等,不是已经有了一个叫做 "),s("code",[e._v("guess")]),e._v(" 的变量了吗?确实如此,不过 Rust 允许用一个新值来 "),s("strong",[e._v("隐藏")]),e._v(" ("),s("em",[e._v("shadow")]),e._v(") "),s("code",[e._v("guess")]),e._v(" 之前的值。这个功能常用在需要转换值类型之类的场景。它允许我们复用 "),s("code",[e._v("guess")]),e._v(" 变量的名字,而不是被迫创建两个不同变量,诸如 "),s("code",[e._v("guess_str")]),e._v(" 和 "),s("code",[e._v("guess")]),e._v(" 之类。(第三章会介绍 shadowing 的更多细节。)")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("我们将 "),s("code",[e._v("guess")]),e._v(" 绑定到 "),s("code",[e._v("guess.trim().parse()")]),e._v(" 表达式上。表达式中的 "),s("code",[e._v("guess")]),e._v(" 是包含输入的原始 "),s("code",[e._v("String")]),e._v(" 类型。"),s("code",[e._v("String")]),e._v(" 实例的 "),s("code",[e._v("trim")]),e._v(" 方法会去除字符串开头和结尾的空白字符。"),s("code",[e._v("u32")]),e._v(" 只能由数字字符转换,不过用户必须输入 "),s("span",{staticClass:"keystroke"},[e._v("enter")]),e._v(" 键才能让 "),s("code",[e._v("read_line")]),e._v(" 返回,然而用户按下 "),s("span",{staticClass:"keystroke"},[e._v("enter")]),e._v(" 键时,会在字符串中增加一个换行(newline)符。例如,用户输入 "),s("span",{staticClass:"keystroke"},[e._v("5")]),e._v(" 并按下 "),s("span",{staticClass:"keystroke"},[e._v("enter")]),e._v(","),s("code",[e._v("guess")]),e._v(" 看起来像这样:"),s("code",[e._v("5\\n")]),e._v("。"),s("code",[e._v("\\n")]),e._v(" 代表 “换行”,回车键。"),s("code",[e._v("trim")]),e._v(" 方法消除 "),s("code",[e._v("\\n")]),e._v(",只留下 "),s("code",[e._v("5")]),e._v("。")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[s("code",[e._v("parse")]),e._v(" 调用很容易产生错误。例如,字符串中包含 "),s("code",[e._v("A👍%")]),e._v(",就无法将其转换为一个数字。因此,"),s("code",[e._v("parse")]),e._v(" 方法返回一个 "),s("code",[e._v("Result")]),e._v(" 类型。像之前 “使用 "),s("code",[e._v("Result")]),e._v(" 类型来处理潜在的错误” 讨论的 "),s("code",[e._v("read_line")]),e._v(" 方法那样,再次按部就班的用 "),s("code",[e._v("expect")]),e._v(" 方法处理即可。如果 "),s("code",[e._v("parse")]),e._v(" 不能从字符串生成一个数字,返回一个 "),s("code",[e._v("Result")]),e._v(" 的 "),s("code",[e._v("Err")]),e._v(" 成员时,"),s("code",[e._v("expect")]),e._v(" 会使游戏崩溃并打印附带的信息。如果 "),s("code",[e._v("parse")]),e._v(" 成功地将字符串转换为一个数字,它会返回 "),s("code",[e._v("Result")]),e._v(" 的 "),s("code",[e._v("Ok")]),e._v(" 成员,然后 "),s("code",[e._v("expect")]),e._v(" 会返回 "),s("code",[e._v("Ok")]),e._v(" 值中的数字。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo run\n Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\n Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs\n Running `target/debug/guessing_game`\nGuess the number!\nThe secret number is: 58\nPlease input your guess.\n 76\nYou guessed: 76\nToo big!\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"使用循环来允许多次猜测"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#使用循环来允许多次猜测","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用循环来允许多次猜测")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("code",[this._v("loop")]),this._v(" 关键字创建了一个无限循环。将其加入后,用户可以反复猜测:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('// --snip--\n\n println!("The secret number is: {}", secret_number);\n\n loop {\n println!("Please input your guess.");\n\n // --snip--\n\n match guess.cmp(&secret_number) {\n Ordering::Less => println!("Too small!"),\n Ordering::Greater => println!("Too big!"),\n Ordering::Equal => println!("You win!"),\n }\n }\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("用户总能使用 "),t("span",{staticClass:"keystroke"},[this._v("ctrl-c")]),this._v(" 终止程序。不过还有另一个方法跳出无限循环,就是 “比较猜测与秘密数字” 部分提到的 "),t("code",[this._v("parse")]),this._v(":如果用户输入的答案不是一个数字,程序会崩溃。用户可以利用这一点来退出,如下所示:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo run\n Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\n Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs\n Running `target/debug/guessing_game`\nGuess the number!\nThe secret number is: 59\nPlease input your guess.\n45\nYou guessed: 45\nToo small!\nPlease input your guess.\n60\nYou guessed: 60\nToo big!\nPlease input your guess.\n59\nYou guessed: 59\nYou win!\nPlease input your guess.\nquit\nthread 'main' panicked at 'Please type a number!: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:785\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\nerror: Process didn't exit successfully: `target/debug/guess` (exit code: 101)\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("输入 "),t("code",[this._v("quit")]),this._v(" 确实退出了程序,同时其他任何非数字输入也一样。然而,这并不理想,我们想要当猜测正确的数字时游戏能自动退出。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"猜测正确后退出"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#猜测正确后退出","aria-hidden":"true"}},[this._v("#")]),this._v(" 猜测正确后退出")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("让我们增加一个 "),t("code",[this._v("break")]),this._v(",在用户猜对时退出游戏:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('// --snip--\n\n match guess.cmp(&secret_number) {\n Ordering::Less => println!("Too small!"),\n Ordering::Greater => println!("Too big!"),\n Ordering::Equal => {\n println!("You win!");\n break;\n }\n }\n }\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("通过在 "),t("code",[this._v("You win!")]),this._v(" 之后增加一行 "),t("code",[this._v("break")]),this._v(",用户猜对了神秘数字后会退出循环。退出循环也意味着退出程序,因为循环是 "),t("code",[this._v("main")]),this._v(" 的最后一部分。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"处理无效输入"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#处理无效输入","aria-hidden":"true"}},[this._v("#")]),this._v(" 处理无效输入")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("为了进一步改善游戏性,不要在用户输入非数字时崩溃,需要忽略非数字,让用户可以继续猜测。可以通过修改 "),t("code",[this._v("guess")]),this._v(" 将 "),t("code",[this._v("String")]),this._v(" 转化为 "),t("code",[this._v("u32")]),this._v(" 那部分代码来实现,如示例 2-5 所示:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('// --snip--\n\nio::stdin().read_line(&mut guess)\n .expect("Failed to read line");\n\nlet guess: u32 = match guess.trim().parse() {\n Ok(num) => num,\n Err(_) => continue,\n};\n\nprintln!("You guessed: {}", guess);\n\n// --snip--\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 2-5: 忽略非数字的猜测并重新请求数字而不是让程序崩溃")])])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("将 "),s("code",[e._v("expect")]),e._v(" 调用换成 "),s("code",[e._v("match")]),e._v(" 语句,是从遇到错误就崩溃转换到真正处理错误的惯用方法。须知 "),s("code",[e._v("parse")]),e._v(" 返回一个 "),s("code",[e._v("Result")]),e._v(" 类型,而 "),s("code",[e._v("Result")]),e._v(" 是一个拥有 "),s("code",[e._v("Ok")]),e._v(" 或 "),s("code",[e._v("Err")]),e._v(" 成员的枚举。这里使用的 "),s("code",[e._v("match")]),e._v(" 表达式,和之前处理 "),s("code",[e._v("cmp")]),e._v(" 方法返回 "),s("code",[e._v("Ordering")]),e._v(" 时用的一样。")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("如果 "),s("code",[e._v("parse")]),e._v(" 能够成功的将字符串转换为一个数字,它会返回一个包含结果数字的 "),s("code",[e._v("Ok")]),e._v("。这个 "),s("code",[e._v("Ok")]),e._v(" 值与 "),s("code",[e._v("match")]),e._v(" 第一个分支的模式相匹配,该分支对应的动作返回 "),s("code",[e._v("Ok")]),e._v(" 值中的数字 "),s("code",[e._v("num")]),e._v(",最后如愿变成新创建的 "),s("code",[e._v("guess")]),e._v(" 变量。")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("p",[e._v("如果 "),s("code",[e._v("parse")]),e._v(" "),s("em",[e._v("不")]),e._v(" 能将字符串转换为一个数字,它会返回一个包含更多错误信息的 "),s("code",[e._v("Err")]),e._v("。"),s("code",[e._v("Err")]),e._v(" 值不能匹配第一个 "),s("code",[e._v("match")]),e._v(" 分支的 "),s("code",[e._v("Ok(num)")]),e._v(" 模式,但是会匹配第二个分支的 "),s("code",[e._v("Err(_)")]),e._v(" 模式:"),s("code",[e._v("_")]),e._v(" 是一个通配符值,本例中用来匹配所有 "),s("code",[e._v("Err")]),e._v(" 值,不管其中有何种信息。所以程序会执行第二个分支的动作,"),s("code",[e._v("continue")]),e._v(" 意味着进入 "),s("code",[e._v("loop")]),e._v(" 的下一次循环,请求另一个猜测。这样程序就有效的忽略了 "),s("code",[e._v("parse")]),e._v(" 可能遇到的所有错误!")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("现在万事俱备,只需运行 "),t("code",[this._v("cargo run")]),this._v(":")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo run\n Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\n Running `target/debug/guessing_game`\nGuess the number!\nThe secret number is: 61\nPlease input your guess.\n10\nYou guessed: 10\nToo small!\nPlease input your guess.\n99\nYou guessed: 99\nToo big!\nPlease input your guess.\nfoo\nPlease input your guess.\n61\nYou guessed: 61\nYou win!\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("太棒了!再有最后一个小的修改,就能完成猜猜看游戏了:还记得程序依然会打印出秘密数字。在测试时还好,但正式发布时会毁了游戏。删掉打印秘密数字的 "),t("code",[this._v("println!")]),this._v("。示例 2-6 为最终代码:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('use std::io;\nuse std::cmp::Ordering;\nuse rand::Rng;\n\nfn main() {\n println!("Guess the number!");\n\n let secret_number = rand::thread_rng().gen_range(1, 101);\n\n loop {\n println!("Please input your guess.");\n\n let mut guess = String::new();\n\n io::stdin().read_line(&mut guess)\n .expect("Failed to read line");\n\n let guess: u32 = match guess.trim().parse() {\n Ok(num) => num,\n Err(_) => continue,\n };\n\n println!("You guessed: {}", guess);\n\n match guess.cmp(&secret_number) {\n Ordering::Less => println!("Too small!"),\n Ordering::Greater => println!("Too big!"),\n Ordering::Equal => {\n println!("You win!");\n break;\n }\n }\n }\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 2-6:猜猜看游戏的完整代码")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"总结"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("本项目通过动手实践,向你介绍了 Rust 新概念:"),t("code",[this._v("let")]),this._v("、"),t("code",[this._v("match")]),this._v("、方法、关联函数、使用外部 crate 等等,接下来的几章,你会继续深入学习这些概念。第三章介绍大部分编程语言都有的概念,比如变量、数据类型和函数,以及如何在 Rust 中使用它们。第四章探索所有权(ownership),这是一个 Rust 同其他语言大不相同的功能。第五章讨论结构体和方法的语法,而第六章侧重解释枚举。")])}],!1,null,null,null);r.options.__file="ch02-00-guessing-game-tutorial.md";t.default=r.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/19.1c2116c8.js b/src/.vuepress/dist/assets/js/19.1c2116c8.js
new file mode 100644
index 0000000..92c3812
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/19.1c2116c8.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[19],{267:function(t,e,s){"use strict";s.r(e);var r=s(0),i=Object(r.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch03-00-common-programming-concepts.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch03-00-common-programming-concepts.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),s("p",[t._v("本章介绍一些几乎所有编程语言都有的概念,以及它们在 Rust 中是如何工作的。很多编程语言的核心概念都是共通的,本章中展示的概念都不是 Rust 所特有的,不过我们会在 Rust 上下文中讨论它们,并解释使用这些概念的惯例。")]),t._v(" "),s("p",[t._v("具体来说,我们将会学习变量、基本类型、函数、注释和控制流。每一个 Rust 程序中都会用到这些基础知识,提早学习这些概念会让你在起步时就打下坚实的基础。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),s("p",[t._v("这里我们将对本书中的一些概念做一些解释:变量、函数、结构体等等。所有这些都需要名称。Rust 中的名称被称为 “标识符”(“identifier”),它们可以是任意非空的 ASCII 字符串,不过有如下限制:")]),t._v(" "),s("p",[t._v("要么是:")]),t._v(" "),t._m(4),t._v(" "),s("p",[t._v("或者是:")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"通用编程概念"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#通用编程概念","aria-hidden":"true"}},[this._v("#")]),this._v(" 通用编程概念")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"关键字"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#关键字","aria-hidden":"true"}},[this._v("#")]),this._v(" 关键字")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Rust 语言有一组保留的 "),e("strong",[this._v("关键字")]),this._v("("),e("em",[this._v("keywords")]),this._v("),就像大部分语言一样,它们只能由语言本身使用。记住,你不能使用这些关键字作为变量或函数的名称。大部分关键字有特殊的意义,你将在 Rust 程序中使用它们完成各种任务;一些关键字目前没有相应的功能,是为将来可能添加的功能保留的。可以在附录 A 中找到关键字的列表。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"标识符"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#标识符","aria-hidden":"true"}},[this._v("#")]),this._v(" 标识符")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("第一个字符是字母。")]),this._v(" "),e("li",[this._v("其它字符是字母数字或者 _。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("第一个字符是 _。")]),this._v(" "),e("li",[this._v("标识符需多于一个字符。单独的 _ 不是标识符。")]),this._v(" "),e("li",[this._v("其它字符是字母数字或者 _。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"原始标识符"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#原始标识符","aria-hidden":"true"}},[this._v("#")]),this._v(" 原始标识符")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("有时出于某种原因你可能需要将关键字作为名称。比如你需要调用 C 语言库中名为 "),e("em",[this._v("match")]),this._v(" 的函数,在 C 语言中 "),e("em",[this._v("match")]),this._v(" 不是关键字。为此你可以使用 “原始标识符”(“raw identifier”)。原始标识符以 "),e("code",[this._v("r#")]),this._v(" 开头:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("let r#fn = \"this variable is named 'fn' even though that's a keyword\";\n\n// 调用名为 'match' 的函数\nr#match();\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("你无需经常用到原始标识符,但是当你 "),e("strong",[this._v("真正")]),this._v(" 需要它们时可以这么做。")])}],!1,null,null,null);i.options.__file="ch03-00-common-programming-concepts.md";e.default=i.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/2.3505b190.js b/src/.vuepress/dist/assets/js/2.3505b190.js
new file mode 100644
index 0000000..d059734
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/2.3505b190.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{164:function(t,e,n){},165:function(t,e,n){"use strict";var a=n(164);n.n(a).a},221:function(t,e,n){"use strict";n.r(e);var a={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,e){var n=e.props,a=e.slots;return t("span",{class:["badge",n.type,n.vertical]},n.text||a().default)}},i=(n(165),n(0)),o=Object(i.a)(a,void 0,void 0,!1,null,"c23f6ad2",null);o.options.__file="Badge.vue";e.default=o.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/20.1f4e3d0f.js b/src/.vuepress/dist/assets/js/20.1f4e3d0f.js
new file mode 100644
index 0000000..ca7b698
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/20.1f4e3d0f.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[20],{265:function(t,s,e){"use strict";e.r(s);var a=e(0),n=Object(a.a)({},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[t._m(0),t._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch03-01-variables-and-mutability.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch03-01-variables-and-mutability.md"),e("OutboundLink")],1),t._v(" "),e("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),e("p",[t._v("第二章中提到过,变量默认是不可改变的(immutable)。这是推动你以充分利用 Rust 提供的安全性和简单并发性来编写代码的众多方式之一。不过,你仍然可以使用可变变量。让我们探讨一下 Rust 拥抱不可变性的原因及方法,以及何时你不想使用不可变性。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),e("p",[t._v("Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要追踪一个值如何和在哪可能会被改变,从而使得代码易于推导。")]),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),e("p",[t._v("现在运行这个程序,出现如下内容:")]),t._v(" "),t._m(14),t._m(15),t._v(" "),e("p",[t._v("除了防止出现 bug 外,还有很多地方需要权衡取舍。例如,使用大型数据结构时,适当地使用可变变量,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的编程风格,可能会使代码更易理解,为可读性而牺牲性能或许是值得的。")]),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),e("p",[t._v("常量可以在任何作用域中声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。")]),t._v(" "),e("p",[t._v("最后一个区别是,常量只能被设置为常量表达式,而不能是函数调用的结果,或任何其他只能在运行时计算出的值。")]),t._v(" "),t._m(20),t._v(" "),t._m(21),e("p",[t._v("在声明它的作用域之中,常量在整个程序生命周期中都有效,这使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。")]),t._v(" "),e("p",[t._v("将遍布于应用程序中的硬编码值声明为常量,能帮助后来的代码维护人员了解值的意图。如果将来需要修改硬编码值,也只需修改汇聚于一处的硬编码值。")]),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),e("p",[t._v("这个错误说明,我们不能改变变量的类型:")]),t._v(" "),t._m(33),e("p",[t._v("现在我们已经了解了变量如何工作,让我们看看变量可以拥有的更多数据类型。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"变量和可变性"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#变量和可变性","aria-hidden":"true"}},[this._v("#")]),this._v(" 变量和可变性")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当变量不可变时,一旦值被绑定一个名称上,你就不能改变这个值。为了对此进行说明,使用 "),s("code",[this._v("cargo new variables")]),this._v(" 命令在 "),s("em",[this._v("projects")]),this._v(" 目录生成一个叫做 "),s("em",[this._v("variables")]),this._v(" 的新项目。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接着,在新建的 "),s("em",[this._v("variables")]),this._v(" 目录,打开 "),s("em",[this._v("src/main.rs")]),this._v(" 并将代码替换为如下代码,这些代码还不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let x = 5;\n println!("The value of x is: {}", x);\n x = 6;\n println!("The value of x is: {}", x);\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("保存并使用 "),s("code",[this._v("cargo run")]),this._v(" 运行程序。应该会看到一条错误信息,如下输出所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0384]: cannot assign twice to immutable variable `x`\n --\x3e src/main.rs:4:5\n |\n2 | let x = 5;\n | - first assignment to `x`\n3 | println!("The value of x is: {}", x);\n4 | x = 6;\n | ^^^^^ cannot assign twice to immutable variable\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个例子展示了编译器如何帮助你找出程序中的错误。虽然编译错误令人沮丧,但那只是表示程序不能安全的完成你想让它完成的工作;并 "),s("strong",[this._v("不能")]),this._v(" 说明你不是一个好程序员!经验丰富的 Rustacean 们一样会遇到编译错误。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("错误信息指出错误的原因是 "),s("code",[this._v("不能对不可变变量 x 二次赋值")]),this._v("("),s("code",[this._v("cannot assign twice to immutable variable x")]),this._v("),因为你尝试对不可变变量 "),s("code",[this._v("x")]),this._v(" 赋第二个值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在尝试改变预设为不可变的值时,产生编译时错误是很重要的,因为这种情况可能导致 bug。如果一部分代码假设一个值永远也不会改变,而另一部分代码改变了这个值,第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 的起因难以跟踪,尤其是第二部分代码只是 "),s("strong",[this._v("有时")]),this._v(" 会改变值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不过可变性也是非常有用的。变量只是默认不可变;正如在第二章所做的那样,你可以在变量名之前加 "),s("code",[this._v("mut")]),this._v(" 来使其可变。除了允许改变值之外,"),s("code",[this._v("mut")]),this._v(" 向读者表明了其他代码将会改变这个变量值的意图。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("例如,让我们将 "),s("em",[this._v("src/main.rs")]),this._v(" 修改为如下代码:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("main")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),e("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" x "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("5")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("println!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token string"}},[t._v('"The value of x is: {}"')]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n x "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("6")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("println!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token string"}},[t._v('"The value of x is: {}"')]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling variables v0.1.0 (file:///projects/variables)\n Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs\n Running `target/debug/variables`\nThe value of x is: 5\nThe value of x is: 6\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("通过 "),e("code",[t._v("mut")]),t._v(",允许把绑定到 "),e("code",[t._v("x")]),t._v(" 的值从 "),e("code",[t._v("5")]),t._v(" 改成 "),e("code",[t._v("6")]),t._v("。在一些情况下,你会想用可变变量,因为与只用不可变变量相比,它会让代码更容易编写。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"变量和常量的区别"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#变量和常量的区别","aria-hidden":"true"}},[this._v("#")]),this._v(" 变量和常量的区别")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不允许改变值的变量,可能会使你想起另一个大部分编程语言都有的概念:"),s("strong",[this._v("常量")]),this._v("("),s("em",[this._v("constants")]),this._v(")。类似于不可变变量,常量是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("首先,不允许对常量使用 "),s("code",[this._v("mut")]),this._v("。常量不光默认不能变,它总是不能变。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("声明常量使用 "),s("code",[this._v("const")]),this._v(" 关键字而不是 "),s("code",[this._v("let")]),this._v(",并且 "),s("em",[this._v("必须")]),this._v(" 注明值的类型。在下一部分,“数据类型” 中会介绍类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这是一个声明常量的例子,它的名称是 "),s("code",[this._v("MAX_POINTS")]),this._v(",值是 100,000。(Rust 常量的命名规范是使用下划线分隔的大写字母单词,并且可以在数字字面值中插入下划线来提升可读性):")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("const")]),t._v(" MAX_POINTS"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32 "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("100_000")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"隐藏(shadowing)"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#隐藏(shadowing)","aria-hidden":"true"}},[this._v("#")]),this._v(" 隐藏(Shadowing)")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("正如在第二章猜猜看游戏的 “比较猜测的数字和秘密数字” 中所讲,我们可以定义一个与之前变量同名的新变量,而新变量会 "),s("strong",[this._v("隐藏")]),this._v(" 之前的变量。Rustacean 们称之为第一个变量被第二个 "),s("strong",[this._v("隐藏")]),this._v(" 了,这意味着使用这个变量时会看到第二个值。可以用相同变量名称来隐藏一个变量,以及重复使用 "),s("code",[this._v("let")]),this._v(" 关键字来多次隐藏,如下所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("main")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("5")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),e("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" x "),e("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("1")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),e("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" x "),e("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("2")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),e("span",{attrs:{class:"token function"}},[t._v("println!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token string"}},[t._v('"The value of x is: {}"')]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("这个程序首先将 "),e("code",[t._v("x")]),t._v(" 绑定到值 "),e("code",[t._v("5")]),t._v(" 上。接着通过 "),e("code",[t._v("let x =")]),t._v(" 隐藏 "),e("code",[t._v("x")]),t._v(",获取初始值并加 "),e("code",[t._v("1")]),t._v(",这样 "),e("code",[t._v("x")]),t._v(" 的值就变成 "),e("code",[t._v("6")]),t._v(" 了。第三个 "),e("code",[t._v("let")]),t._v(" 语句也隐藏了 "),e("code",[t._v("x")]),t._v(",将之前的值乘以 "),e("code",[t._v("2")]),t._v(","),e("code",[t._v("x")]),t._v(" 最终的值是 "),e("code",[t._v("12")]),t._v("。运行这个程序,它会有如下输出:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling variables v0.1.0 (file:///projects/variables)\n Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs\n Running `target/debug/variables`\nThe value of x is: 12\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("隐藏与将变量标记为 "),s("code",[this._v("mut")]),this._v(" 是有区别的。当不小心尝试对变量重新赋值时,如果没有使用 "),s("code",[this._v("let")]),this._v(" 关键字,就会导致编译时错误。通过使用 "),s("code",[this._v("let")]),this._v(",我们可以用这个值进行一些计算,不过计算完之后变量仍然是不变的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("mut")]),this._v(" 与隐藏的另一个区别是,当再次使用 "),s("code",[this._v("let")]),this._v(" 时,实际上创建了一个新变量,我们可以改变值的类型,但复用这个名字。例如,假设程序请求用户输入空格字符来说明希望在文本之间显示多少个空格,然而我们真正需要的是将输入存储成数字(多少个空格):")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" spaces "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{attrs:{class:"token string"}},[t._v('" "')]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),e("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" spaces "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" spaces"),e("span",{attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{attrs:{class:"token function"}},[t._v("len")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("这里允许第一个 "),e("code",[t._v("spaces")]),t._v(" 变量是字符串类型,而第二个 "),e("code",[t._v("spaces")]),t._v(" 变量,它是一个恰巧与第一个变量同名的崭新变量,是数字类型。隐藏使我们不必使用不同的名字,如 "),e("code",[t._v("spaces_str")]),t._v(" 和 "),e("code",[t._v("spaces_num")]),t._v(";相反,我们可以复用 "),e("code",[t._v("spaces")]),t._v(" 这个更简单的名字。然而,如果尝试使用 "),e("code",[t._v("mut")]),t._v(",将会得到一个编译时错误,如下所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let mut spaces = " ";\nspaces = spaces.len();\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0308]: mismatched types\n --\x3e src/main.rs:3:14\n |\n3 | spaces = spaces.len();\n | ^^^^^^^^^^^^ expected &str, found usize\n |\n = note: expected type `&str`\n found type `usize`\n")])])])}],!1,null,null,null);n.options.__file="ch03-01-variables-and-mutability.md";s.default=n.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/21.80dca6df.js b/src/.vuepress/dist/assets/js/21.80dca6df.js
new file mode 100644
index 0000000..67e9d34
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/21.80dca6df.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[21],{264:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch03-02-data-types.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch03-02-data-types.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),a("p",[t._v("这里如果不添加类型注解,Rust 会显示如下错误,这说明编译器需要我们提供更多信息,来了解我们想要的类型:")]),t._v(" "),t._m(4),a("p",[t._v("你会看到其它数据类型的各种类型注解。")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),a("p",[t._v("这是一个展示浮点数的实例:")]),t._v(" "),t._m(23),t._v(" "),t._m(24),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),a("p",[t._v("这些语句中的每个表达式使用了一个数学运算符并计算出了一个值,然后绑定给一个变量。附录 B 包含 Rust 提供的所有运算符的列表。")]),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),t._v(" "),a("p",[t._v("元组是一个将多个其他类型的值组合进一个复合类型的主要方式。")]),t._v(" "),a("p",[t._v("我们使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了可选的类型注解:")]),t._v(" "),t._m(43),t._v(" "),t._m(44),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._m(48),t._v(" "),t._m(49),t._v(" "),t._m(50),t._v(" "),t._m(51),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._v(" "),a("p",[t._v("Rust 中,数组中的值位于中括号内的逗号分隔的列表中:")]),t._v(" "),t._m(55),t._v(" "),t._m(56),t._m(57),t._v(" "),a("p",[t._v("一个你可能想要使用数组而不是 vector 的例子是,当程序需要知道一年中月份的名字时。程序不大可能会去增加或减少月份。这时你可以使用数组,因为我们知道它总是包含 12 个元素:")]),t._v(" "),t._m(58),t._m(59),t._v(" "),t._m(60),a("p",[t._v("首先是方括号;这看起来像创建数组的语法。其中有两部分由分号分割的信息。第一部分是数组中每个元素的类型。因为所有元素都是相同类型的,所以只需列出一次。分号之后,是一个表示数组长度的数字。因为数组是固定长度的,该数字也一直保持不变,即便数组的元素被修改,它也不会增长或缩小。")]),t._v(" "),t._m(61),t._v(" "),a("p",[t._v("数组是一整块分配在栈上的内存。可以使用索引来访问数组的元素,像这样:")]),t._v(" "),t._m(62),t._v(" "),t._m(63),t._m(64),t._v(" "),t._m(65),t._v(" "),a("p",[t._v("如果我们访问数组结尾之后的元素会发生什么呢?比如你将上面的例子改成下面这样,这可以编译不过在运行时会因错误而退出:")]),t._v(" "),t._m(66),t._v(" "),t._m(67),t._m(68),t._v(" "),t._m(69),t._m(70),t._v(" "),a("p",[t._v("这是第一个在实战中遇到的 Rust 安全原则的例子。在很多底层语言中,并没有进行这类检查,这样当提供了一个不正确的索引时,就会访问无效的内存。通过立即退出而不是允许内存访问并继续执行,Rust 让你避开此类错误。第九章会讨论更多 Rust 的错误处理。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"数据类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#数据类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 数据类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 Rust 中,每一个值都属于某一个 "),s("strong",[this._v("数据类型")]),this._v("("),s("em",[this._v("data type")]),this._v("),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)和复合(compound)。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("记住,Rust 是 "),a("strong",[t._v("静态类型")]),t._v("("),a("em",[t._v("statically typed")]),t._v(")语言,也就是说在编译时就必须知道所有变量的类型。根据值及其使用方式,编译器通常可以推断出我们想要用的类型。当多种类型均有可能时,比如第二章的 “比较猜测的数字和秘密数字” 使用 "),a("code",[t._v("parse")]),t._v(" 将 "),a("code",[t._v("String")]),t._v(" 转换为数字时,必须增加类型注解,像这样:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" guess"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"42"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("parse")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("expect")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Not a number!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0282]: type annotations needed\n --\x3e src/main.rs:2:9\n |\n2 | let guess = "42".parse().expect("Not a number!");\n | ^^^^^\n | |\n | cannot infer type for `_`\n | consider giving `guess` a type\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"标量类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#标量类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 标量类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("标量")]),this._v("("),s("em",[this._v("scalar")]),this._v(")类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。你可能在其他语言中见过它们。让我们深入了解它们在 Rust 中是如何工作的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"整型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#整型","aria-hidden":"true"}},[this._v("#")]),this._v(" 整型")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("strong",[t._v("整数")]),t._v(" 是一个没有小数部分的数字。我们在第二章使用过 "),a("code",[t._v("u32")]),t._v(" 整数类型。该类型声明表明,它关联的值应该是一个占据 32 比特位的无符号整数(有符号整数类型以 "),a("code",[t._v("i")]),t._v(" 开头而不是 "),a("code",[t._v("u")]),t._v(")。表格 3-1 展示了 Rust 内建的整数类型。在有符号列和无符号列中的每一个变体(例如,"),a("code",[t._v("i16")]),t._v(")都可以用来声明整数值的类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("表格 3-1: Rust 中的整型")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("table",[a("thead",[a("tr",[a("th",[t._v("长度")]),t._v(" "),a("th",[t._v("有符号")]),t._v(" "),a("th",[t._v("无符号")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[t._v("8-bit")]),t._v(" "),a("td",[a("code",[t._v("i8")])]),t._v(" "),a("td",[a("code",[t._v("u8")])])]),t._v(" "),a("tr",[a("td",[t._v("16-bit")]),t._v(" "),a("td",[a("code",[t._v("i16")])]),t._v(" "),a("td",[a("code",[t._v("u16")])])]),t._v(" "),a("tr",[a("td",[t._v("32-bit")]),t._v(" "),a("td",[a("code",[t._v("i32")])]),t._v(" "),a("td",[a("code",[t._v("u32")])])]),t._v(" "),a("tr",[a("td",[t._v("64-bit")]),t._v(" "),a("td",[a("code",[t._v("i64")])]),t._v(" "),a("td",[a("code",[t._v("u64")])])]),t._v(" "),a("tr",[a("td",[t._v("arch")]),t._v(" "),a("td",[a("code",[t._v("isize")])]),t._v(" "),a("td",[a("code",[t._v("usize")])])])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("每一个变体都可以是有符号或无符号的,并有一个明确的大小。"),s("strong",[this._v("有符号")]),this._v(" 和 "),s("strong",[this._v("无符号")]),this._v(" 代表数字能否为负值,换句话说,数字是否需要有一个符号(有符号数),或者永远为正而不需要符号(无符号数)。这有点像在纸上书写数字:当需要考虑符号的时候,数字以加号或减号作为前缀;然而,可以安全地假设为正数时,加号前缀通常省略。有符号数以补码形式(two’s complement representation)存储(如果你不清楚这是什么,可以在网上搜索;对其的解释超出了本书的范畴)。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("每一个有符号的变体可以储存包含从 -(2"),a("sup",[t._v("n - 1")]),t._v(") 到 2"),a("sup",[t._v("n - 1")]),t._v(" - 1 在内的数字,这里 "),a("em",[t._v("n")]),t._v(" 是变体使用的位数。所以 "),a("code",[t._v("i8")]),t._v(" 可以储存从 -(2"),a("sup",[t._v("7")]),t._v(") 到 2"),a("sup",[t._v("7")]),t._v(" - 1 在内的数字,也就是从 -128 到 127。无符号的变体可以储存从 0 到 2"),a("sup",[t._v("n")]),t._v(" - 1 的数字,所以 "),a("code",[t._v("u8")]),t._v(" 可以储存从 0 到 2"),a("sup",[t._v("8")]),t._v(" - 1 的数字,也就是从 0 到 255。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("另外,"),s("code",[this._v("isize")]),this._v(" 和 "),s("code",[this._v("usize")]),this._v(" 类型依赖运行程序的计算机架构:64 位架构上它们是 64 位的, 32 位架构上它们是 32 位的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以使用表格 3-2 中的任何一种形式编写数字字面值。注意除 byte 以外的所有数字字面值允许使用类型后缀,例如 "),s("code",[this._v("57u8")]),this._v(",同时也允许使用 "),s("code",[this._v("_")]),this._v(" 做为分隔符以方便读数,例如"),s("code",[this._v("1_000")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("表格 3-2: Rust 中的整型字面值")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("table",[a("thead",[a("tr",[a("th",[t._v("数字字面值")]),t._v(" "),a("th",[t._v("例子")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[t._v("Decimal")]),t._v(" "),a("td",[a("code",[t._v("98_222")])])]),t._v(" "),a("tr",[a("td",[t._v("Hex")]),t._v(" "),a("td",[a("code",[t._v("0xff")])])]),t._v(" "),a("tr",[a("td",[t._v("Octal")]),t._v(" "),a("td",[a("code",[t._v("0o77")])])]),t._v(" "),a("tr",[a("td",[t._v("Binary")]),t._v(" "),a("td",[a("code",[t._v("0b1111_0000")])])]),t._v(" "),a("tr",[a("td",[t._v("Byte ("),a("code",[t._v("u8")]),t._v(" only)")]),t._v(" "),a("td",[a("code",[t._v("b'A'")])])])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("那么该使用哪种类型的数字呢?如果拿不定主意,Rust 的默认类型通常就很好,数字类型默认是 "),s("code",[this._v("i32")]),this._v(":它通常是最快的,甚至在 64 位系统上也是。"),s("code",[this._v("isize")]),this._v(" 或 "),s("code",[this._v("usize")]),this._v(" 主要作为某些集合的索引。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h5",{attrs:{id:"整型溢出"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#整型溢出","aria-hidden":"true"}},[this._v("#")]),this._v(" 整型溢出")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("比方说有一个 "),a("code",[t._v("u8")]),t._v(" ,它可以存放从零到 "),a("code",[t._v("255")]),t._v(" 的值。那么当你将其修改为 "),a("code",[t._v("256")]),t._v(" 时会发生什么呢?这被称为 “整型溢出”(“integer overflow” ),关于这一行为 Rust 有一些有趣的规则。当在 debug 模式编译时,Rust 检查这类问题并使程序 "),a("em",[t._v("panic")]),t._v(",这个术语被 Rust 用来表明程序因错误而退出。第九章会详细介绍 panic。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("在 release 构建中,Rust 不检测溢出,相反会进行一种被称为 “two’s complement wrapping” 的操作。简而言之,"),a("code",[t._v("256")]),t._v(" 变成 "),a("code",[t._v("0")]),t._v(","),a("code",[t._v("257")]),t._v(" 变成 "),a("code",[t._v("1")]),t._v(",依此类推。依赖溢出被认为是一种错误,即便可能出现这种行为。如果你确实需要这种行为,标准库中有一个类型显式提供此功能,"),a("code",[t._v("Wrapping")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"浮点型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#浮点型","aria-hidden":"true"}},[this._v("#")]),this._v(" 浮点型")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("Rust 也有两个原生的 "),a("strong",[t._v("浮点数")]),t._v("("),a("em",[t._v("floating-point numbers")]),t._v(")类型,它们是带小数点的数字。Rust 的浮点数类型是 "),a("code",[t._v("f32")]),t._v(" 和 "),a("code",[t._v("f64")]),t._v(",分别占 32 位和 64 位。默认类型是 "),a("code",[t._v("f64")]),t._v(",因为在现代 CPU 中,它与 "),a("code",[t._v("f32")]),t._v(" 速度几乎一样,不过精度更高。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2.0")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// f64")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" f32 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3.0")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// f32")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("浮点数采用 IEEE-754 标准表示。"),s("code",[this._v("f32")]),this._v(" 是单精度浮点数,"),s("code",[this._v("f64")]),this._v(" 是双精度浮点数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"数值运算"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#数值运算","aria-hidden":"true"}},[this._v("#")]),this._v(" 数值运算")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘法、除法和取余。下面的代码展示了如何在 "),s("code",[this._v("let")]),this._v(" 语句中使用它们:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// 加法")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" sum "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("10")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// 减法")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" difference "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("95.5")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4.3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// 乘法")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" product "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("30")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// 除法")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" quotient "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("56.7")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("/")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("32.2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// 取余")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" remainder "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("43")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"布尔型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#布尔型","aria-hidden":"true"}},[this._v("#")]),this._v(" 布尔型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("正如其他大部分编程语言一样,Rust 中的布尔类型有两个可能的值:"),s("code",[this._v("true")]),this._v(" 和 "),s("code",[this._v("false")]),this._v("。Rust 中的布尔类型使用 "),s("code",[this._v("bool")]),this._v(" 表示。例如:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" t "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("true")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" f"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("false")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// 显式指定类型注解")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用布尔值的主要场景是条件表达式,例如 "),s("code",[this._v("if")]),this._v(" 表达式。在 “控制流”(“Control Flow”)部分将介绍 "),s("code",[this._v("if")]),this._v(" 表达式在 Rust 中如何工作。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"字符类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#字符类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 字符类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("目前为止只使用到了数字,不过 Rust 也支持字母。Rust 的 "),s("code",[this._v("char")]),this._v(" 类型是语言中最原生的字母类型,如下代码展示了如何使用它。(注意 "),s("code",[this._v("char")]),this._v(" 由单引号指定,不同于字符串使用双引号。)")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" c "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token char string"}},[t._v("'z'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" z "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token char string"}},[t._v("'ℤ'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" heart_eyed_cat "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'😻")]),a("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("';")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("Rust 的 "),a("code",[t._v("char")]),t._v(" 类型代表了一个 Unicode 标量值(Unicode Scalar Value),这意味着它可以比 ASCII 表示更多内容。在 Rust 中,拼音字母(Accented letters),中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的 "),a("code",[t._v("char")]),t._v(" 值。Unicode 标量值包含从 "),a("code",[t._v("U+0000")]),t._v(" 到 "),a("code",[t._v("U+D7FF")]),t._v(" 和 "),a("code",[t._v("U+E000")]),t._v(" 到 "),a("code",[t._v("U+10FFFF")]),t._v(" 在内的值。不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的 "),a("code",[t._v("char")]),t._v(" 并不符合。第八章的 “字符串” 中将详细讨论这个主题。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"复合类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#复合类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 复合类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("复合类型")]),this._v("("),s("em",[this._v("Compound types")]),this._v(")可以将多个值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"元组类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#元组类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 元组类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" tup"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f64"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" u8"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("500")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("6.4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("tup")]),this._v(" 变量绑定到整个元组上,因为元组是一个单独的复合元素。为了从元组中获取单个值,可以使用模式匹配(pattern matching)来解构(destructure)元组值,像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" tup "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("500")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("6.4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" z"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" tup"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"The value of y is: {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("程序首先创建了一个元组并绑定到 "),a("code",[t._v("tup")]),t._v(" 变量上。接着使用了 "),a("code",[t._v("let")]),t._v(" 和一个模式将 "),a("code",[t._v("tup")]),t._v(" 分成了三个不同的变量,"),a("code",[t._v("x")]),t._v("、"),a("code",[t._v("y")]),t._v(" 和 "),a("code",[t._v("z")]),t._v("。这叫做 "),a("strong",[t._v("解构")]),t._v("("),a("em",[t._v("destructuring")]),t._v("),因为它将一个元组拆成了三个部分。最后,程序打印出了 "),a("code",[t._v("y")]),t._v(" 的值,也就是 "),a("code",[t._v("6.4")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("除了使用模式匹配解构外,也可以使用点号("),s("code",[this._v(".")]),this._v(")后跟值的索引来直接访问它们。例如:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f64"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" u8"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("500")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("6.4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" five_hundred "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" x"),a("span",{attrs:{class:"token number"}},[t._v(".0")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" six_point_four "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" x"),a("span",{attrs:{class:"token number"}},[t._v(".1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" one "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" x"),a("span",{attrs:{class:"token number"}},[t._v(".2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个程序创建了一个元组,"),s("code",[this._v("x")]),this._v(",并接着使用索引为每个元素创建新变量。跟大多数编程语言一样,元组的第一个索引值是 0。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"数组类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#数组类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 数组类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("另一个包含多个值的方式是 "),s("strong",[this._v("数组")]),this._v("("),s("em",[this._v("array")]),this._v(")。与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,因为 Rust 中的数组是固定长度的:一旦声明,它们的长度不能增长或缩小。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" a "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当你想要在栈(stack)而不是在堆(heap)上为数据分配空间(第四章将讨论栈与堆的更多内容),或者是想要确保总是有固定数量的元素时,数组非常有用。但是数组并不如 vector 类型灵活。vector 类型是标准库提供的一个 "),s("strong",[this._v("允许")]),this._v(" 增长和缩小长度的类似数组的集合类型。当不确定是应该使用数组还是 vector 的时候,你可能应该使用 vector。第八章会详细讨论 vector。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" months "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token string"}},[t._v('"January"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"February"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"March"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"April"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"May"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"June"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"July"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"August"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"September"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"October"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"November"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"December"')]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("数组的类型比较有趣;它看起来像 "),s("code",[this._v("[type; number]")]),this._v("。例如:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" a"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h5",{attrs:{id:"访问数组元素"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#访问数组元素","aria-hidden":"true"}},[this._v("#")]),this._v(" 访问数组元素")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" a "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" first "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" second "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("在这个例子中,叫做 "),a("code",[t._v("first")]),t._v(" 的变量的值是 "),a("code",[t._v("1")]),t._v(",因为它是数组索引 "),a("code",[t._v("[0]")]),t._v(" 的值。变量 "),a("code",[t._v("second")]),t._v(" 将会是数组索引 "),a("code",[t._v("[1]")]),t._v(" 的值 "),a("code",[t._v("2")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h5",{attrs:{id:"无效的数组元素访问"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#无效的数组元素访问","aria-hidden":"true"}},[this._v("#")]),this._v(" 无效的数组元素访问")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let a = [1, 2, 3, 4, 5];\n let index = 10;\n\n let element = a[index];\n\n println!("The value of element is: {}", element);\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用 "),s("code",[this._v("cargo run")]),this._v(" 运行代码后会产生如下结果:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling arrays v0.1.0 (file:///projects/arrays)\n Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs\n Running `target/debug/arrays`\nthread '' panicked at 'index out of bounds: the len is 5 but the index is\n 10', src/main.rs:6\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("编译并没有产生任何错误,不过程序会出现一个 "),s("strong",[this._v("运行时")]),this._v("("),s("em",[this._v("runtime")]),this._v(")错误并且不会成功退出。当尝试用索引访问一个元素时,Rust 会检查指定的索引是否小于数组的长度。如果索引超出了数组长度,Rust 会 "),s("em",[this._v("panic")]),this._v(",这是 Rust 术语,它用于程序因为错误而退出的情况。")])}],!1,null,null,null);e.options.__file="ch03-02-data-types.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/22.24977f4c.js b/src/.vuepress/dist/assets/js/22.24977f4c.js
new file mode 100644
index 0000000..100cee9
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/22.24977f4c.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[22],{262:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch03-03-how-functions-work.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch03-03-how-functions-work.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),n("p",[t._v("尝试运行程序,将会输出如下内容:")]),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),n("p",[t._v("当一个函数有多个参数时,使用逗号分隔,像这样:")]),t._v(" "),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._v(" "),n("p",[t._v("函数体由一系列的语句和一个可选的结尾表达式构成。目前为止,我们只介绍了没有结尾表达式的函数,不过你已经见过作为语句一部分的表达式。因为 Rust 是一门基于表达式(expression-based)的语言,这是一个需要理解的(不同于其他语言)重要区别。其他语言并没有这样的区别,所以让我们看看语句与表达式有什么区别以及这些区别是如何影响函数体的。")]),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._m(29),t._v(" "),n("p",[t._v("函数定义也是语句,上面整个例子本身就是一个语句。")]),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),n("p",[t._v("当运行这个程序时,会得到如下错误:")]),t._v(" "),t._m(33),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),n("p",[t._v("这个表达式:")]),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._m(44),t._v(" "),t._m(45),t._m(46),t._v(" "),t._m(47),t._m(48),t._v(" "),n("p",[t._v("让我们看看另一个例子:")]),t._v(" "),t._m(49),t._v(" "),t._m(50),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),n("p",[t._v("运行代码会产生一个错误,如下:")]),t._v(" "),t._m(54),t._m(55)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 函数")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("函数遍布于 Rust 代码中。你已经见过语言中最重要的函数之一:"),s("code",[this._v("main")]),this._v(" 函数,它是很多程序的入口点。你也见过 "),s("code",[this._v("fn")]),this._v(" 关键字,它用来声明新函数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 代码中的函数和变量名使用 "),s("em",[this._v("snake case")]),this._v(" 规范风格。在 snake case 中,所有字母都是小写并使用下划线分隔单词。这是一个包含函数定义示例的程序:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Hello, world!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("another_function")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("another_function")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Another function."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 中的函数定义以 "),s("code",[this._v("fn")]),this._v(" 开始并在函数名后跟一对圆括号。大括号告诉编译器哪里是函数体的开始和结尾。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("可以使用函数名后跟圆括号来调用我们定义过的任意函数。因为程序中已定义 "),n("code",[t._v("another_function")]),t._v(" 函数,所以可以在 "),n("code",[t._v("main")]),t._v(" 函数中调用它。注意,源码中 "),n("code",[t._v("another_function")]),t._v(" 定义在 "),n("code",[t._v("main")]),t._v(" 函数 "),n("strong",[t._v("之后")]),t._v(";也可以定义在之前。Rust 不关心函数定义于何处,只要定义了就行。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们新建一个叫做 "),s("em",[this._v("functions")]),this._v(" 的二进制项目来进一步探索函数。将上面的 "),s("code",[this._v("another_function")]),this._v(" 例子写入 "),s("em",[this._v("src/main.rs")]),this._v(" 中并运行。你应该会看到如下输出:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling functions v0.1.0 (file:///projects/functions)\n Finished dev [unoptimized + debuginfo] target(s) in 0.28 secs\n Running `target/debug/functions`\nHello, world!\nAnother function.\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("main")]),this._v(" 函数中的代码会按顺序执行。首先,打印 “Hello, world!” 信息,然后调用 "),s("code",[this._v("another_function")]),this._v(" 函数并打印它的信息。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"函数参数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#函数参数","aria-hidden":"true"}},[this._v("#")]),this._v(" 函数参数")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("函数也可以被定义为拥有 "),n("strong",[t._v("参数")]),t._v("("),n("em",[t._v("parameters")]),t._v("),参数是特殊变量,是函数签名的一部分。当函数拥有参数(形参)时,可以为这些参数提供具体的值(实参)。技术上讲,这些具体值被称为参数("),n("em",[t._v("arguments")]),t._v("),但是在日常交流中,人们倾向于不区分使用 "),n("em",[t._v("parameter")]),t._v(" 和 "),n("em",[t._v("argument")]),t._v(" 来表示函数定义中的变量或调用函数时传入的具体值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("下面被重写的 "),s("code",[this._v("another_function")]),this._v(" 版本展示了 Rust 中参数是什么样的:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("another_function")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("another_function")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The value of x is: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling functions v0.1.0 (file:///projects/functions)\n Finished dev [unoptimized + debuginfo] target(s) in 1.21 secs\n Running `target/debug/functions`\nThe value of x is: 5\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("another_function")]),t._v(" 的声明中有一个命名为 "),n("code",[t._v("x")]),t._v(" 的参数。"),n("code",[t._v("x")]),t._v(" 的类型被指定为 "),n("code",[t._v("i32")]),t._v("。当将 "),n("code",[t._v("5")]),t._v(" 传给 "),n("code",[t._v("another_function")]),t._v(" 时,"),n("code",[t._v("println!")]),t._v(" 宏将 "),n("code",[t._v("5")]),t._v(" 放入格式化字符串中大括号的位置。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在函数签名中,"),s("strong",[this._v("必须")]),this._v(" 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解,意味着编译器不需要你在代码的其他地方注明类型来指出你的意图。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("another_function")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("6")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("another_function")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The value of x is: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The value of y is: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个例子创建了有两个参数的函数,都是 "),s("code",[this._v("i32")]),this._v(" 类型。函数打印出了这两个参数的值。注意函数的参数类型并不一定相同,这个例子中只是碰巧相同罢了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("尝试运行代码。使用上面的例子替换当前 "),s("em",[this._v("functions")]),this._v(" 项目的 "),s("em",[this._v("src/main.rs")]),this._v(" 文件,并用 "),s("code",[this._v("cargo run")]),this._v(" 运行它:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling functions v0.1.0 (file:///projects/functions)\n Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs\n Running `target/debug/functions`\nThe value of x is: 5\nThe value of y is: 6\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("因为我们使用 "),n("code",[t._v("5")]),t._v(" 作为 "),n("code",[t._v("x")]),t._v(" 的值,"),n("code",[t._v("6")]),t._v(" 作为 "),n("code",[t._v("y")]),t._v(" 的值来调用函数,因此打印出这两个字符串及相应的值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"包含语句和表达式的函数体"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#包含语句和表达式的函数体","aria-hidden":"true"}},[this._v("#")]),this._v(" 包含语句和表达式的函数体")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("实际上,我们已经使用过语句和表达式。"),s("strong",[this._v("语句")]),this._v("("),s("em",[this._v("Statements")]),this._v(")是执行一些操作但不返回值的指令。表达式("),s("em",[this._v("Expressions")]),this._v(")计算并产生一个值。让我们看一些例子:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用 "),s("code",[this._v("let")]),this._v(" 关键字创建变量并绑定一个值是一个语句。在列表 3-1 中,"),s("code",[this._v("let y = 6;")]),this._v(" 是一个语句。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" y "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("6")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 3-1:包含一个语句的 "),s("code",[this._v("main")]),this._v(" 函数定义")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("语句不返回值。因此,不能把 "),s("code",[this._v("let")]),this._v(" 语句赋值给另一个变量,比如下面的例子尝试做的,会产生一个错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn main() {\n let x = (let y = 6);\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling functions v0.1.0 (file:///projects/functions)\nerror: expected expression, found statement (`let`)\n --\x3e src/main.rs:2:14\n |\n2 | let x = (let y = 6);\n | ^^^\n |\n = note: variable declaration using `let` is a statement\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("let y = 6")]),t._v(" 语句并不返回值,所以没有可以绑定到 "),n("code",[t._v("x")]),t._v(" 上的值。这与其他语言不同,例如 C 和 Ruby,它们的赋值语句会返回所赋的值。在这些语言中,可以这么写 "),n("code",[t._v("x = y = 6")]),t._v(",这样 "),n("code",[t._v("x")]),t._v(" 和 "),n("code",[t._v("y")]),t._v(" 的值都是 "),n("code",[t._v("6")]),t._v(";Rust 中不能这样写。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("表达式会计算出一些值,并且你将编写的大部分 Rust 代码是由表达式组成的。考虑一个简单的数学运算,比如 "),n("code",[t._v("5 + 6")]),t._v(",这是一个表达式并计算出值 "),n("code",[t._v("11")]),t._v("。表达式可以是语句的一部分:在示例 3-1 中,语句 "),n("code",[t._v("let y = 6;")]),t._v(" 中的 "),n("code",[t._v("6")]),t._v(" 是一个表达式,它计算出的值是 "),n("code",[t._v("6")]),t._v("。函数调用是一个表达式。宏调用是一个表达式。我们用来创建新作用域的大括号(代码块),"),n("code",[t._v("{}")]),t._v(",也是一个表达式,例如:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" y "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n x "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The value of y is: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("{\n let x = 3;\n x + 1\n}\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("是一个代码块,它的值是 "),n("code",[t._v("4")]),t._v("。这个值作为 "),n("code",[t._v("let")]),t._v(" 语句的一部分被绑定到 "),n("code",[t._v("y")]),t._v(" 上。注意结尾没有分号的那一行 "),n("code",[t._v("x+1")]),t._v(",与你见过的大部分代码行不同。表达式的结尾没有分号。如果在表达式的结尾加上分号,它就变成了语句,而语句不会返回值。在接下来探索具有返回值的函数和表达式时要谨记这一点。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"具有返回值的函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#具有返回值的函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 具有返回值的函数")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("函数可以向调用它的代码返回值。我们并不对返回值命名,但要在箭头("),s("code",[this._v("->")]),this._v(")后声明它的类型。在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。使用 "),s("code",[this._v("return")]),this._v(" 关键字和指定值,可从函数中提前返回;但大部分函数隐式的返回最后的表达式。这是一个有返回值的函数的例子:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("five")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token number"}},[t._v("5")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("five")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The value of x is: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 "),n("code",[t._v("five")]),t._v(" 函数中没有函数调用、宏、甚至没有 "),n("code",[t._v("let")]),t._v(" 语句——只有数字 "),n("code",[t._v("5")]),t._v("。这在 Rust 中是一个完全有效的函数。注意,也指定了函数返回值的类型,就是 "),n("code",[t._v("-> i32")]),t._v("。尝试运行代码;输出应该看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling functions v0.1.0 (file:///projects/functions)\n Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs\n Running `target/debug/functions`\nThe value of x is: 5\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("five")]),t._v(" 函数的返回值是 "),n("code",[t._v("5")]),t._v(",所以返回值类型是 "),n("code",[t._v("i32")]),t._v("。让我们仔细检查一下这段代码。有两个重要的部分:首先,"),n("code",[t._v("let x = five();")]),t._v(" 这一行表明我们使用函数的返回值初始化一个变量。因为 "),n("code",[t._v("five")]),t._v(" 函数返回 "),n("code",[t._v("5")]),t._v(",这一行与如下代码相同:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[this._v("let")]),this._v(" x "),s("span",{attrs:{class:"token operator"}},[this._v("=")]),this._v(" "),s("span",{attrs:{class:"token number"}},[this._v("5")]),s("span",{attrs:{class:"token punctuation"}},[this._v(";")]),this._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("其次,"),s("code",[this._v("five")]),this._v(" 函数没有参数并定义了返回值类型,不过函数体只有单单一个 "),s("code",[this._v("5")]),this._v(" 也没有分号,因为这是一个表达式,我们想要返回它的值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("plus_one")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The value of x is: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("plus_one")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("运行代码会打印出 "),s("code",[this._v("The value of x is: 6")]),this._v("。但如果在包含 "),s("code",[this._v("x + 1")]),this._v(" 的行尾加上一个分号,把它从表达式变成语句,我们将看到一个错误。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let x = plus_one(5);\n\n println!("The value of x is: {}", x);\n}\n\nfn plus_one(x: i32) -> i32 {\n x + 1;\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0308]: mismatched types\n --\x3e src/main.rs:7:28\n |\n7 | fn plus_one(x: i32) -> i32 {\n | ____________________________^\n8 | | x + 1;\n | | - help: consider removing this semicolon\n9 | | }\n | |_^ expected i32, found ()\n |\n = note: expected type `i32`\n found type `()`\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("主要的错误信息,“mismatched types”(类型不匹配),揭示了代码的核心问题。函数 "),s("code",[this._v("plus_one")]),this._v(" 的定义说明它要返回一个 "),s("code",[this._v("i32")]),this._v(" 类型的值,不过语句并不会返回值,使用空元组 "),s("code",[this._v("()")]),this._v(" 表示不返回值。因为不返回值与函数定义相矛盾,从而出现一个错误。在输出中,Rust 提供了一条信息,可能有助于纠正这个错误:它建议删除分号,这会修复这个错误。")])}],!1,null,null,null);e.options.__file="ch03-03-how-functions-work.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/23.867b53ea.js b/src/.vuepress/dist/assets/js/23.867b53ea.js
new file mode 100644
index 0000000..7d87bb1
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/23.867b53ea.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[23],{261:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch03-04-comments.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch03-04-comments.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),n("p",[t._v("这是一个简单的注释:")]),t._v(" "),t._m(2),t._m(3),t._v(" "),t._m(4),n("p",[t._v("注释也可以在放在包含代码的行的末尾:")]),t._v(" "),t._m(5),t._v(" "),t._m(6),n("p",[t._v("不过你更经常看到以这种格式使用它们,也就是位于它所解释的代码行的上面一行:")]),t._v(" "),t._m(7),t._v(" "),t._m(8),n("p",[t._v("Rust 还有另一种注释,称为文档注释,我们将在 14 章讨论它。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"注释"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#注释","aria-hidden":"true"}},[this._v("#")]),this._v(" 注释")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("所有程序员都力求使其代码易于理解,不过有时还需要提供额外的解释。在这种情况下,程序员在源码中留下记录,或者 "),s("strong",[this._v("注释")]),this._v("("),s("em",[this._v("comments")]),this._v("),编译器会忽略它们,不过阅读代码的人可能觉得有用。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token comment"}},[this._v("// hello, world")]),this._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 Rust 中,注释必须以两道斜杠开始,并持续到本行的结尾。对于超过一行的注释,需要在每一行前都加上 "),s("code",[this._v("//")]),this._v(",像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token comment"}},[this._v("// So we’re doing something complicated here, long enough that we need")]),this._v("\n"),s("span",{attrs:{class:"token comment"}},[this._v("// multiple lines of comments to do it! Whew! Hopefully, this comment will")]),this._v("\n"),s("span",{attrs:{class:"token comment"}},[this._v("// explain what’s going on.")]),this._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" lucky_number "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("7")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// I’m feeling lucky today")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// I’m feeling lucky today")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" lucky_number "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("7")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])}],!1,null,null,null);e.options.__file="ch03-04-comments.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/24.d59d9203.js b/src/.vuepress/dist/assets/js/24.d59d9203.js
new file mode 100644
index 0000000..b562cc2
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/24.d59d9203.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[24],{259:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch03-05-control-flow.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch03-05-control-flow.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),n("p",[t._v("尝试运行代码,应该能看到如下输出:")]),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),n("p",[t._v("再次运行程序并查看输出:")]),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),n("p",[t._v("这个程序有四个可能的执行路径。运行后应该能看到如下输出:")]),t._v(" "),t._m(26),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._m(50),t._v(" "),t._m(51),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),t._v(" "),t._m(56),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._v(" "),t._m(60),t._v(" "),t._m(61),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._v(" "),t._m(65),t._v(" "),t._m(66),t._v(" "),t._m(67),t._m(68),t._v(" "),t._m(69),t._v(" "),t._m(70),t._m(71),t._v(" "),n("p",[t._v("但这个过程很容易出错;如果索引长度不正确会导致程序 panic。这也使程序更慢,因为编译器增加了运行时代码来对每次循环的每个元素进行条件检查。")]),t._v(" "),t._m(72),t._v(" "),t._m(73),t._v(" "),t._m(74),t._m(75),t._v(" "),n("p",[t._v("当运行这段代码时,将看到与示例 3-4 一样的输出。更为重要的是,我们增强了代码安全性,并消除了可能由于超出数组的结尾或遍历长度不够而缺少一些元素而导致的 bug。")]),t._v(" "),t._m(76),t._v(" "),t._m(77),t._v(" "),t._m(78),t._v(" "),t._m(79),t._v(" "),t._m(80),n("p",[t._v("这段代码看起来更帅气不是吗?")]),t._v(" "),t._m(81),t._v(" "),t._m(82),t._v(" "),t._m(83),t._v(" "),t._m(84)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"控制流"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#控制流","aria-hidden":"true"}},[this._v("#")]),this._v(" 控制流")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("根据条件是否为真来决定是否执行某些代码,以及根据条件是否为真来重复运行一段代码是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 "),s("code",[this._v("if")]),this._v(" 表达式和循环。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"if-表达式"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#if-表达式","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("if")]),this._v(" 表达式")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("if")]),this._v(" 表达式允许根据条件执行不同的代码分支。你提供一个条件并表示 “如果条件满足,运行这段代码;如果条件不满足,不运行这段代码。”")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 "),n("em",[t._v("projects")]),t._v(" 目录新建一个叫做 "),n("em",[t._v("branches")]),t._v(" 的项目,来学习 "),n("code",[t._v("if")]),t._v(" 表达式。在 "),n("em",[t._v("src/main.rs")]),t._v(" 文件中,输入如下内容:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"condition was true"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"condition was false"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("所有的 "),n("code",[t._v("if")]),t._v(" 表达式都以 "),n("code",[t._v("if")]),t._v(" 关键字开头,其后跟一个条件。在这个例子中,条件检查变量 "),n("code",[t._v("number")]),t._v(" 的值是否小于 5。在条件为真时希望执行的代码块位于紧跟条件之后的大括号中。"),n("code",[t._v("if")]),t._v(" 表达式中与条件关联的代码块有时被叫做 "),n("em",[t._v("arms")]),t._v(",就像第二章 “比较猜测的数字和秘密数字” 部分中讨论到的 "),n("code",[t._v("match")]),t._v(" 表达式中的分支一样。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("也可以包含一个可选的 "),s("code",[this._v("else")]),this._v(" 表达式来提供一个在条件为假时应当执行的代码块,这里我们就这么做了。如果不提供 "),s("code",[this._v("else")]),this._v(" 表达式并且条件为假时,程序会直接忽略 "),s("code",[this._v("if")]),this._v(" 代码块并继续执行下面的代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling branches v0.1.0 (file:///projects/branches)\n Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs\n Running `target/debug/branches`\ncondition was true\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("尝试改变 "),s("code",[this._v("number")]),this._v(" 的值使条件为假时看看会发生什么:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("let number = 7;\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling branches v0.1.0 (file:///projects/branches)\n Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs\n Running `target/debug/branches`\ncondition was false\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("另外值得注意的是代码中的条件 "),s("strong",[this._v("必须")]),this._v(" 是 "),s("code",[this._v("bool")]),this._v(" 值。如果条件不是 "),s("code",[this._v("bool")]),this._v(" 值,我们将得到一个错误。例如,尝试运行以下代码:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let number = 3;\n\n if number {\n println!("number was three");\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里 "),s("code",[this._v("if")]),this._v(" 条件的值是 "),s("code",[this._v("3")]),this._v(",Rust 抛出了一个错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0308]: mismatched types\n --\x3e src/main.rs:4:8\n |\n4 | if number {\n | ^^^^^^ expected bool, found integral variable\n |\n = note: expected type `bool`\n found type `{integer}`\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这个错误表明 Rust 期望一个 "),n("code",[t._v("bool")]),t._v(" 却得到了一个整数。不像 Ruby 或 JavaScript 这样的语言,Rust 并不会尝试自动地将非布尔值转换为布尔值。必须总是显式地使用布尔值作为 "),n("code",[t._v("if")]),t._v(" 的条件。例如,如果想要 "),n("code",[t._v("if")]),t._v(" 代码块只在一个数字不等于 "),n("code",[t._v("0")]),t._v(" 时执行,可以把 "),n("code",[t._v("if")]),t._v(" 表达式修改成下面这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"number was something other than zero"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("运行代码会打印出 "),s("code",[this._v("number was something other than zero")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用-else-if-处理多重条件"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-else-if-处理多重条件","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("else if")]),this._v(" 处理多重条件")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以将 "),s("code",[this._v("else if")]),this._v(" 表达式与 "),s("code",[this._v("if")]),this._v(" 和 "),s("code",[this._v("else")]),this._v(" 组合来实现多重条件。例如:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("6")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("4")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"number is divisible by 4"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"number is divisible by 3"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"number is divisible by 2"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"number is not divisible by 4, 3, or 2"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling branches v0.1.0 (file:///projects/branches)\n Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs\n Running `target/debug/branches`\nnumber is divisible by 3\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当执行这个程序时,它按顺序检查每个 "),n("code",[t._v("if")]),t._v(" 表达式并执行第一个条件为真的代码块。注意即使 6 可以被 2 整除,也不会输出 "),n("code",[t._v("number is divisible by 2")]),t._v(",更不会输出 "),n("code",[t._v("else")]),t._v(" 块中的 "),n("code",[t._v("number is not divisible by 4, 3, or 2")]),t._v("。原因是 Rust 只会执行第一个条件为真的代码块,并且一旦它找到一个以后,甚至都不会检查剩下的条件了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用过多的 "),s("code",[this._v("else if")]),this._v(" 表达式会使代码显得杂乱无章,所以如果有多于一个 "),s("code",[this._v("else if")]),this._v(" 表达式,最好重构代码。为此,第六章会介绍一个强大的 Rust 分支结构(branching construct),叫做 "),s("code",[this._v("match")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"在-let-语句中使用-if"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#在-let-语句中使用-if","aria-hidden":"true"}},[this._v("#")]),this._v(" 在 "),s("code",[this._v("let")]),this._v(" 语句中使用 "),s("code",[this._v("if")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为 "),s("code",[this._v("if")]),this._v(" 是一个表达式,我们可以在 "),s("code",[this._v("let")]),this._v(" 语句的右侧使用它,例如在示例 3-2 中:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" condition "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("true")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" condition "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token number"}},[t._v("5")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token number"}},[t._v("6")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The value of number is: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" number"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 3-2:将 "),s("code",[this._v("if")]),this._v(" 表达式的返回值赋给一个变量")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("number")]),this._v(" 变量将会绑定到表示 "),s("code",[this._v("if")]),this._v(" 表达式结果的值上。运行这段代码看看会出现什么:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling branches v0.1.0 (file:///projects/branches)\n Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs\n Running `target/debug/branches`\nThe value of number is: 5\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("记住,代码块的值是其最后一个表达式的值,而数字本身就是一个表达式。在这个例子中,整个 "),n("code",[t._v("if")]),t._v(" 表达式的值取决于哪个代码块被执行。这意味着 "),n("code",[t._v("if")]),t._v(" 的每个分支的可能的返回值都必须是相同类型;在示例 3-2 中,"),n("code",[t._v("if")]),t._v(" 分支和 "),n("code",[t._v("else")]),t._v(" 分支的结果都是 "),n("code",[t._v("i32")]),t._v(" 整型。如果它们的类型不匹配,如下面这个例子,则会出现一个错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let condition = true;\n\n let number = if condition {\n 5\n } else {\n "six"\n };\n\n println!("The value of number is: {}", number);\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当编译这段代码时,会得到一个错误。"),s("code",[this._v("if")]),this._v(" 和 "),s("code",[this._v("else")]),this._v(" 分支的值类型是不相容的,同时 Rust 也准确地指出在程序中的何处发现的这个问题:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0308]: if and else have incompatible types\n --\x3e src/main.rs:4:18\n |\n4 | let number = if condition {\n | __________________^\n5 | | 5\n6 | | } else {\n7 | | "six"\n8 | | };\n | |_____^ expected integral variable, found &str\n |\n = note: expected type `{integer}`\n found type `&str`\n')])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("if")]),t._v(" 代码块中的表达式返回一个整数,而 "),n("code",[t._v("else")]),t._v(" 代码块中的表达式返回一个字符串。这不可行,因为变量必须只有一个类型。Rust 需要在编译时就确切的知道 "),n("code",[t._v("number")]),t._v(" 变量的类型,这样它就可以在编译时验证在每处使用的 "),n("code",[t._v("number")]),t._v(" 变量的类型是有效的。Rust 并不能够在 "),n("code",[t._v("number")]),t._v(" 的类型只能在运行时确定的情况下工作;这样会使编译器变得更复杂而且只能为代码提供更少的保障,因为它不得不记录所有变量的多种可能的类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用循环重复执行"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用循环重复执行","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用循环重复执行")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("多次执行同一段代码是很常用的,Rust 为此提供了多种 "),s("strong",[this._v("循环")]),this._v("("),s("em",[this._v("loops")]),this._v(")。一个循环执行循环体中的代码直到结尾并紧接着回到开头继续执行。为了实验一下循环,让我们新建一个叫做 "),s("em",[this._v("loops")]),this._v(" 的项目。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 有三种循环:"),s("code",[this._v("loop")]),this._v("、"),s("code",[this._v("while")]),this._v(" 和 "),s("code",[this._v("for")]),this._v("。我们每一个都试试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用-loop-重复执行代码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-loop-重复执行代码","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("loop")]),this._v(" 重复执行代码")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("loop")]),this._v(" 关键字告诉 Rust 一遍又一遍地执行一段代码直到你明确要求停止。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("作为一个例子,将 "),s("em",[this._v("loops")]),this._v(" 目录中的 "),s("em",[this._v("src/main.rs")]),this._v(" 文件修改为如下:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n loop {\n println!("again!");\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当运行这个程序时,我们会看到连续的反复打印 "),s("code",[this._v("again!")]),this._v(",直到我们手动停止程序。大部分终端都支持一个快捷键,"),s("span",{staticClass:"keystroke"},[this._v("ctrl-c")]),this._v(",来终止一个陷入无限循环的程序。尝试一下:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling loops v0.1.0 (file:///projects/loops)\n Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs\n Running `target/debug/loops`\nagain!\nagain!\nagain!\nagain!\n^Cagain!\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("符号 "),n("code",[t._v("^C")]),t._v(" 代表你在这按下了"),n("span",{staticClass:"keystroke"},[t._v("ctrl-c")]),t._v("。在 "),n("code",[t._v("^C")]),t._v(" 之后你可能看到也可能看不到 "),n("code",[t._v("again!")]),t._v(" ,这取决于在接收到终止信号时代码执行到了循环的何处。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("幸运的是,Rust 提供了另一种更可靠的退出循环的方式。可以使用 "),s("code",[this._v("break")]),this._v(" 关键字来告诉程序何时停止循环。回忆一下在第二章猜猜看游戏的 “猜测正确后退出” 部分使用过它来在用户猜对数字赢得游戏后退出程序。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"从循环返回"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#从循环返回","aria-hidden":"true"}},[this._v("#")]),this._v(" 从循环返回")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("loop")]),this._v(" 的一个用例是重试可能会失败的操作,比如检查线程是否完成了任务。然而你可能会需要将操作的结果传递给其它的代码。如果将返回值加入你用来停止循环的 "),s("code",[this._v("break")]),this._v(" 表达式,它会被停止的循环返回:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" counter "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" result "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("loop")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n counter "),n("span",{attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" counter "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("break")]),t._v(" counter "),n("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("20")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"while-条件循环"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#while-条件循环","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("while")]),this._v(" 条件循环")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在程序中计算循环的条件也很常见。当条件为真,执行循环。当条件不再为真,调用 "),n("code",[t._v("break")]),t._v(" 停止循环。这个循环类型可以通过组合 "),n("code",[t._v("loop")]),t._v("、"),n("code",[t._v("if")]),t._v("、"),n("code",[t._v("else")]),t._v(" 和 "),n("code",[t._v("break")]),t._v(" 来实现;如果你喜欢的话,现在就可以在程序中试试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("然而,这个模式太常用了,Rust 为此内置了一个语言结构,它被称为 "),s("code",[this._v("while")]),this._v(" 循环。示例 3-3 使用了 "),s("code",[this._v("while")]),this._v(":程序循环三次,每次数字都减一。接着,在循环结束后,打印出另一个信息并退出。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("while")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"{}!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" number"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n number "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"LIFTOFF!!!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 3-3: 当条件为真时,使用 "),s("code",[this._v("while")]),this._v(" 循环运行代码")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这种结构消除了很多使用 "),n("code",[t._v("loop")]),t._v("、"),n("code",[t._v("if")]),t._v("、"),n("code",[t._v("else")]),t._v(" 和 "),n("code",[t._v("break")]),t._v(" 时所必须的嵌套,这样更加清晰。当条件为真就执行,否则退出循环。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用-for-遍历集合"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-for-遍历集合","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("for")]),this._v(" 遍历集合")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以使用 "),s("code",[this._v("while")]),this._v(" 结构来遍历集合中的元素,比如数组。例如,看看示例 3-4。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" a "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("20")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("30")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("40")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("50")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" index "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("while")]),t._v(" index "),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"the value is: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("index"),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n index "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" index "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 3-4:使用 "),s("code",[this._v("while")]),this._v(" 循环遍历集合中的元素")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里,代码对数组中的元素进行计数。它从索引 "),s("code",[this._v("0")]),this._v(" 开始,并接着循环直到遇到数组的最后一个索引(这时,"),s("code",[this._v("index < 5")]),this._v(" 不再为真)。运行这段代码会打印出数组中的每一个元素:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run\n Compiling loops v0.1.0 (file:///projects/loops)\n Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs\n Running `target/debug/loops`\nthe value is: 10\nthe value is: 20\nthe value is: 30\nthe value is: 40\nthe value is: 50\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("数组中的所有五个元素都如期被打印出来。尽管 "),s("code",[this._v("index")]),this._v(" 在某一时刻会到达值 "),s("code",[this._v("5")]),this._v(",不过循环在其尝试从数组获取第六个值(会越界)之前就停止了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("作为更简洁的替代方案,可以使用 "),s("code",[this._v("for")]),this._v(" 循环来对一个集合的每个元素执行一些代码。"),s("code",[this._v("for")]),this._v(" 循环看起来如示例 3-5 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" a "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("20")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("30")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("40")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("50")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" element "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" a"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"the value is: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" element"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 3-5:使用 "),s("code",[this._v("for")]),this._v(" 循环遍历集合中的元素")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("例如,在示例 3-4 的代码中,如果从数组 "),s("code",[this._v("a")]),this._v(" 中移除一个元素但忘记将条件更新为 "),s("code",[this._v("while index < 4")]),this._v(",代码将会 panic。使用 "),s("code",[this._v("for")]),this._v(" 循环的话,就不需要惦记着在改变数组元素个数时修改其他的代码了。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("for")]),t._v(" 循环的安全性和简洁性使得它成为 Rust 中使用最多的循环结构。即使是在想要循环执行代码特定次数时,例如示例 3-3 中使用 "),n("code",[t._v("while")]),t._v(" 循环的倒计时例子,大部分 Rustacean 也会使用 "),n("code",[t._v("for")]),t._v(" 循环。这么做的方式是使用 "),n("code",[t._v("Range")]),t._v(",它是标准库提供的类型,用来生成从一个数字开始到另一个数字之前结束的所有数字的序列。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("下面是一个使用 "),s("code",[this._v("for")]),this._v(" 循环来倒计时的例子,它还使用了一个我们还未讲到的方法,"),s("code",[this._v("rev")]),this._v(",用来反转 range:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" number "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v("..")]),n("span",{attrs:{class:"token number"}},[t._v("4")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("rev")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"{}!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" number"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"LIFTOFF!!!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("你做到了!这是一个大章节:你学习了变量、标量和复合数据类型、函数、注释、 "),s("code",[this._v("if")]),this._v(" 表达式和循环!如果你想要实践本章讨论的概念,尝试构建如下程序:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[this._v("相互转换摄氏与华氏温度。")]),this._v(" "),s("li",[this._v("生成 n 阶斐波那契数列。")]),this._v(" "),s("li",[this._v("打印圣诞颂歌 “The Twelve Days of Christmas” 的歌词,并利用歌曲中的重复部分(编写循环)。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当你准备好继续的时候,让我们讨论一个其他语言中 "),s("em",[this._v("并不")]),this._v(" 常见的概念:所有权(ownership)。")])}],!1,null,null,null);e.options.__file="ch03-05-control-flow.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/25.fae2b839.js b/src/.vuepress/dist/assets/js/25.fae2b839.js
new file mode 100644
index 0000000..f868bfc
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/25.fae2b839.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{258:function(t,e,s){"use strict";s.r(e);var n=s(0),r=Object(n.a)({},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[this._m(0),this._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch04-00-understanding-ownership.md",target:"_blank",rel:"noopener noreferrer"}},[this._v("ch04-00-understanding-ownership.md"),e("OutboundLink")],1),this._v(" "),e("br"),this._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),this._v(" "),e("p",[this._v("所有权(系统)是 Rust 最独特的功能,其令 Rust 无需垃圾回收(garbage collector)即可保障内存安全。因此,理解 Rust 中所有权如何工作是十分重要的。本章,我们将讲到所有权以及相关功能:借用、slice 以及 Rust 如何在内存中布局数据。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"认识所有权"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#认识所有权","aria-hidden":"true"}},[this._v("#")]),this._v(" 认识所有权")])}],!1,null,null,null);r.options.__file="ch04-00-understanding-ownership.md";e.default=r.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/26.ba769edc.js b/src/.vuepress/dist/assets/js/26.ba769edc.js
new file mode 100644
index 0000000..730e1f7
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/26.ba769edc.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{256:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch04-01-what-is-ownership.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch04-01-what-is-ownership.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),n("p",[t._v("所有运行的程序都必须管理其使用计算机内存的方式。一些语言中具有垃圾回收机制,在程序运行时不断地寻找不再使用的内存;在另一些语言中,程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。在运行时,所有权系统的任何功能都不会减慢程序。")]),t._v(" "),n("p",[t._v("因为所有权对很多程序员来说都是一个新概念,需要一些时间来适应。好消息是随着你对 Rust 和所有权系统的规则越来越有经验,你就越能自然地编写出安全和高效的代码。持之以恒!")]),t._v(" "),n("p",[t._v("当你理解了所有权,你将有一个坚实的基础来理解那些使 Rust 独特的功能。在本章中,你将通过完成一些示例来学习所有权,这些示例基于一个常用的数据结构:字符串。")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),n("p",[t._v("首先,让我们看一下所有权的规则。当我们通过举例说明时,请谨记这些规则:")]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._m(11),t._v(" "),n("p",[t._v("换句话说,这里有两个重要的时间点:")]),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),n("p",[t._v("为了演示所有权的规则,我们需要一个比第三章 “数据类型” 中讲到的都要复杂的数据类型。前面介绍的类型都是存储在栈上的并且当离开作用域时被移出栈,不过我们需要寻找一个存储在堆上的数据来探索 Rust 是如何知道该在何时清理数据的。")]),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._m(21),t._v(" "),t._m(22),t._v(" "),n("p",[t._v("就字符串字面值来说,我们在编译时就知道其内容,所以文本被直接硬编码进最终的可执行文件中。这使得字符串字面值快速且高效。不过这些特性都只得益于字符串字面值的不可变性。不幸的是,我们不能为了每一个在编译时大小未知的文本而将一块内存放入二进制文件中,并且它的大小还可能随着程序运行而改变。")]),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._m(29),t._v(" "),t._m(30),t._v(" "),n("p",[t._v("这个模式对编写 Rust 代码的方式有着深远的影响。现在它看起来很简单,不过在更复杂的场景下代码的行为可能是不可预测的,比如当有多个变量使用在堆上分配的内存时。现在让我们探索一些这样的场景。")]),t._v(" "),t._m(31),t._v(" "),n("p",[t._v("Rust 中的多个变量可以采用一种独特的方式与同一数据交互。让我们看看示例 4-2 中一个使用整型的例子。")]),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._m(37),t._v(" "),t._m(38),t._v(" "),n("img",{staticClass:"center",staticStyle:{width:"50%"},attrs:{alt:"String in memory",src:"img/trpl04-01.svg"}}),t._v(" "),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),n("img",{staticClass:"center",staticStyle:{width:"50%"},attrs:{alt:"s1 and s2 pointing to the same value",src:"img/trpl04-02.svg"}}),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),n("img",{staticClass:"center",staticStyle:{width:"50%"},attrs:{alt:"s1 and s2 to two places",src:"img/trpl04-03.svg"}}),t._v(" "),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),n("p",[t._v("你会得到一个类似如下的错误,因为 Rust 禁止你使用无效的引用。")]),t._v(" "),t._m(48),t._m(49),t._v(" "),n("img",{staticClass:"center",staticStyle:{width:"50%"},attrs:{alt:"s1 moved to s2",src:"img/trpl04-04.svg"}}),t._v(" "),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),t._v(" "),t._m(56),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._v(" "),n("p",[t._v("这里还有一个没有提到的小窍门。这些代码使用了整型并且是有效的,他们是示例 4-2 中的一部分:")]),t._v(" "),t._m(60),t._m(61),t._v(" "),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._v(" "),t._m(65),t._v(" "),t._m(66),t._v(" "),n("p",[t._v("将值传递给函数在语义上与给变量赋值相似。向函数传递值可能会移动或者复制,就像赋值语句一样。示例 4-3 使用注释展示变量何时进入和离开作用域:")]),t._v(" "),t._m(67),t._v(" "),t._m(68),t._m(69),t._v(" "),t._m(70),t._v(" "),t._m(71),t._v(" "),n("p",[t._v("返回值也可以转移所有权。示例 4-4 与示例 4-3 一样带有类似的注释。")]),t._v(" "),t._m(72),t._v(" "),t._m(73),t._m(74),t._v(" "),t._m(75),t._v(" "),n("p",[t._v("在每一个函数中都获取所有权并接着返回所有权有些啰嗦。如果我们想要函数使用一个值但不获取所有权该怎么办呢?如果我们还要接着使用它的话,每次都传进去再返回来就有点烦人了,除此之外,我们也可能想返回函数体中产生的一些数据。")]),t._v(" "),n("p",[t._v("我们可以使用元组来返回多个值,如示例 4-5 所示。")]),t._v(" "),t._m(76),t._v(" "),t._m(77),t._m(78),t._v(" "),t._m(79)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"什么是所有权"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#什么是所有权","aria-hidden":"true"}},[this._v("#")]),this._v(" 什么是所有权")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 的核心功能(之一)是 "),s("strong",[this._v("所有权")]),this._v("("),s("em",[this._v("ownership")]),this._v(")。虽然该功能很容易解释,但它对语言的其他部分有着深刻的影响。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("blockquote",[n("h3",{attrs:{id:"栈(stack)与堆(heap)"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#栈(stack)与堆(heap)","aria-hidden":"true"}},[t._v("#")]),t._v(" 栈(Stack)与堆(Heap)")]),t._v(" "),n("p",[t._v("在很多语言中,你并不需要经常考虑到栈与堆。不过在像 Rust 这样的系统编程语言中,值是位于栈上还是堆上在更大程度上影响了语言的行为以及为何必须做出这样的抉择。我们会在本章的稍后部分描述所有权与栈和堆相关的内容,所以这里只是一个用来预热的简要解释。")]),t._v(" "),n("p",[t._v("栈和堆都是代码在运行时可供使用的内存,但是它们的结构不同。栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 "),n("strong",[t._v("后进先出")]),t._v("("),n("em",[t._v("last in, first out")]),t._v(")。想象一下一叠盘子:当增加更多盘子时,把它们放在盘子堆的顶部,当需要盘子时,也从顶部拿走。不能从中间也不能从底部增加或拿走盘子!增加数据叫做 "),n("strong",[t._v("进栈")]),t._v("("),n("em",[t._v("pushing onto the stack")]),t._v("),而移出数据叫做 "),n("strong",[t._v("出栈")]),t._v("("),n("em",[t._v("popping off the stack")]),t._v(")。")]),t._v(" "),n("p",[t._v("栈的操作是十分快速的,这主要是得益于它存取数据的方式:因为数据存取的位置总是在栈顶而不需要寻找一个位置存放或读取数据。另一个让操作栈快速的属性是,栈中的所有数据都必须占用已知且固定的大小。")]),t._v(" "),n("p",[t._v("在编译时大小未知或大小可能变化的数据,要改为存储在堆上。堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。操作系统在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的 "),n("strong",[t._v("指针")]),t._v("("),n("em",[t._v("pointer")]),t._v(")。这个过程称作 "),n("strong",[t._v("在堆上分配内存")]),t._v("("),n("em",[t._v("allocating on the heap")]),t._v("),有时简称为 “分配”(allocating)。将数据推入栈中并不被认为是分配。因为指针的大小是已知并且固定的,你可以将指针存储在栈上,不过当需要实际数据时,必须访问指针。")]),t._v(" "),n("p",[t._v("想象一下去餐馆就座吃饭。当进入时,你说明有几个人,餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了,他们也可以通过询问来找到你们坐在哪。")]),t._v(" "),n("p",[t._v("访问堆上的数据比访问栈上的数据慢,因为必须通过指针来访问。现代处理器在内存中跳转越少就越快(缓存)。继续类比,假设有一个服务员在餐厅里处理多个桌子的点菜。在一个桌子报完所有菜后再移动到下一个桌子是最有效率的。从桌子 A 听一个菜,接着桌子 B 听一个菜,然后再桌子 A,然后再桌子 B 这样的流程会更加缓慢。出于同样原因,处理器在处理的数据彼此较近的时候(比如在栈上)比较远的时候(比如可能在堆上)能更好的工作。在堆上分配大量的空间也可能消耗时间。")]),t._v(" "),n("p",[t._v("当你的代码调用一个函数时,传递给函数的值(包括可能指向堆上数据的指针)和函数的局部变量被压入栈中。当函数结束时,这些值被移出栈。")]),t._v(" "),n("p",[t._v("跟踪哪部分代码正在使用堆上的哪些数据,最大限度的减少堆上的重复数据的数量,以及清理堆上不再使用的数据确保不会耗尽空间,这些问题正是所有权系统要处理的。一旦理解了所有权,你就不需要经常考虑栈和堆了,不过明白了所有权的存在就是为了管理堆数据,能够帮助解释为什么所有权要以这种方式工作。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"所有权规则"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#所有权规则","aria-hidden":"true"}},[this._v("#")]),this._v(" 所有权规则")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("blockquote",[n("ol",[n("li",[t._v("Rust 中的每一个值都有一个被称为其 "),n("strong",[t._v("所有者")]),t._v("("),n("em",[t._v("owner")]),t._v(")的变量。")]),t._v(" "),n("li",[t._v("值有且只有一个所有者。")]),t._v(" "),n("li",[t._v("当所有者(变量)离开作用域,这个值将被丢弃。")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"变量作用域"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#变量作用域","aria-hidden":"true"}},[this._v("#")]),this._v(" 变量作用域")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们已经在第二章完成一个 Rust 程序示例。既然我们已经掌握了基本语法,将不会在之后的例子中包含 "),s("code",[this._v("fn main() {")]),this._v(" 代码,所以如果你是一路跟过来的,必须手动将之后例子的代码放入一个 "),s("code",[this._v("main")]),this._v(" 函数中。这样,例子将显得更加简明,使我们可以关注实际细节而不是样板代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在所有权的第一个例子中,我们看看一些变量的 "),s("strong",[this._v("作用域")]),this._v("("),s("em",[this._v("scope")]),this._v(")。作用域是一个项(item)在程序中有效的范围。假设有这样一个变量:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[this._v("let")]),this._v(" s "),s("span",{attrs:{class:"token operator"}},[this._v("=")]),this._v(" "),s("span",{attrs:{class:"token string"}},[this._v('"hello"')]),s("span",{attrs:{class:"token punctuation"}},[this._v(";")]),this._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("变量 "),s("code",[this._v("s")]),this._v(" 绑定到了一个字符串字面值,这个字符串值是硬编码进程序代码中的。这个变量从声明的点开始直到当前 "),s("strong",[this._v("作用域")]),this._v(" 结束时都是有效的。示例 4-1 的注释标明了变量 "),s("code",[this._v("s")]),this._v(" 在何处是有效的。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// s 在这里无效, 它尚未声明")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 从此处起,s 是有效的")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 使用 s")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 此作用域已结束,s 不再有效")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 4-1:一个变量和其有效的作用域")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ul",[n("li",[t._v("当 "),n("code",[t._v("s")]),t._v(" "),n("strong",[t._v("进入作用域")]),t._v(" 时,它就是有效的。")]),t._v(" "),n("li",[t._v("这一直持续到它 "),n("strong",[t._v("离开作用域")]),t._v(" 为止。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("目前为止,变量是否有效与作用域的关系跟其他编程语言是类似的。现在我们在此基础上介绍 "),s("code",[this._v("String")]),this._v(" 类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"string-类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#string-类型","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("String")]),this._v(" 类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里使用 "),s("code",[this._v("String")]),this._v(" 作为例子,并专注于 "),s("code",[this._v("String")]),this._v(" 与所有权相关的部分。这些方面也同样适用于标准库提供的或你自己创建的其他复杂数据类型。在第八章会更深入地讲解 "),s("code",[this._v("String")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们已经见过字符串字面值,字符串值被硬编码进程序里。字符串字面值是很方便的,不过他们并不适合使用文本的每一种场景。原因之一就是他们是不可变的。另一个原因是并不是所有字符串的值都能在编写代码时就知道:例如,要是想获取用户输入并存储该怎么办呢?为此,Rust 有第二个字符串类型,"),s("code",[this._v("String")]),this._v("。这个类型被分配到堆上,所以能够存储在编译时未知大小的文本。可以使用 "),s("code",[this._v("from")]),this._v(" 函数基于字符串字面值来创建 "),s("code",[this._v("String")]),this._v(",如下:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这两个冒号("),n("code",[t._v("::")]),t._v(")是运算符,允许将特定的 "),n("code",[t._v("from")]),t._v(" 函数置于 "),n("code",[t._v("String")]),t._v(" 类型的命名空间(namespace)下,而不需要使用类似 "),n("code",[t._v("string_from")]),t._v(" 这样的名字。在第五章的 “方法语法”(“Method Syntax”)部分会着重讲解这个语法而且在第七章的 “模块定义” 中会讲到模块的命名空间。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("可以")]),this._v(" 修改此类字符串 :")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\ns"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("push_str")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('", world!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// push_str() 在字符串后追加字面值")]),t._v("\n\n"),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"{}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 将打印 `hello, world!`")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("那么这里有什么区别呢?为什么 "),s("code",[this._v("String")]),this._v(" 可变而字面值却不行呢?区别在于两个类型对内存的处理上。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"内存与分配"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#内存与分配","aria-hidden":"true"}},[this._v("#")]),this._v(" 内存与分配")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("对于 "),s("code",[this._v("String")]),this._v(" 类型,为了支持一个可变,可增长的文本片段,需要在堆上分配一块在编译时未知大小的内存来存放内容。这意味着:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[this._v("必须在运行时向操作系统请求内存。")]),this._v(" "),s("li",[this._v("需要一个当我们处理完 "),s("code",[this._v("String")]),this._v(" 时将内存返回给操作系统的方法。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第一部分由我们完成:当调用 "),s("code",[this._v("String::from")]),this._v(" 时,它的实现 ("),s("em",[this._v("implementation")]),this._v(") 请求其所需的内存。这在编程语言中是非常通用的。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("然而,第二部分实现起来就各有区别了。在有 "),n("strong",[t._v("垃圾回收")]),t._v("("),n("em",[t._v("garbage collector")]),t._v(","),n("em",[t._v("GC")]),t._v(")的语言中, GC 记录并清除不再使用的内存,而我们并不需要关心它。没有 GC 的话,识别出不再使用的内存并调用代码显式释放就是我们的责任了,跟请求内存的时候一样。从历史的角度上说正确处理内存回收曾经是一个困难的编程问题。如果忘记回收了会浪费内存。如果过早回收了,将会出现无效变量。如果重复回收,这也是个 bug。我们需要精确的为一个 "),n("code",[t._v("allocate")]),t._v(" 配对一个 "),n("code",[t._v("free")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 采取了一个不同的策略:内存在拥有它的变量离开作用域后就被自动释放。下面是示例 4-1 中作用域例子的一个使用 "),s("code",[this._v("String")]),this._v(" 而不是字符串字面值的版本:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 从此处起,s 是有效的")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 使用 s")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 此作用域已结束,")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// s 不再有效")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这是一个将 "),n("code",[t._v("String")]),t._v(" 需要的内存返回给操作系统的很自然的位置:当 "),n("code",[t._v("s")]),t._v(" 离开作用域的时候。当变量离开作用域,Rust 为我们调用一个特殊的函数。这个函数叫做 "),n("code",[t._v("drop")]),t._v(",在这里 "),n("code",[t._v("String")]),t._v(" 的作者可以放置释放内存的代码。Rust 在结尾的 "),n("code",[t._v("}")]),t._v(" 处自动调用 "),n("code",[t._v("drop")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("blockquote",[s("p",[this._v("注意:在 C++ 中,这种 item 在生命周期结束时释放资源的模式有时被称作 "),s("strong",[this._v("资源获取即初始化")]),this._v("("),s("em",[this._v("Resource Acquisition Is Initialization (RAII)")]),this._v(")。如果你使用过 RAII 模式的话应该对 Rust 的 "),s("code",[this._v("drop")]),this._v(" 函数并不陌生。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"变量与数据交互的方式(一):移动"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#变量与数据交互的方式(一):移动","aria-hidden":"true"}},[this._v("#")]),this._v(" 变量与数据交互的方式(一):移动")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" y "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 4-2:将变量 "),s("code",[this._v("x")]),this._v(" 的整数值赋给 "),s("code",[this._v("y")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们大致可以猜到这在干什么:“将 "),n("code",[t._v("5")]),t._v(" 绑定到 "),n("code",[t._v("x")]),t._v(";接着生成一个值 "),n("code",[t._v("x")]),t._v(" 的拷贝并绑定到 "),n("code",[t._v("y")]),t._v("”。现在有了两个变量,"),n("code",[t._v("x")]),t._v(" 和 "),n("code",[t._v("y")]),t._v(",都等于 "),n("code",[t._v("5")]),t._v("。这也正是事实上发生了的,因为正数是有已知固定大小的简单值,所以这两个 "),n("code",[t._v("5")]),t._v(" 被放入了栈中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在看看这个 "),s("code",[this._v("String")]),this._v(" 版本:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s2 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s1"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这看起来与上面的代码非常类似,所以我们可能会假设他们的运行方式也是类似的:也就是说,第二行可能会生成一个 "),s("code",[this._v("s1")]),this._v(" 的拷贝并绑定到 "),s("code",[this._v("s2")]),this._v(" 上。不过,事实上并不完全是这样。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("看看图 4-1 以了解 "),s("code",[this._v("String")]),this._v(" 的底层会发生什么。"),s("code",[this._v("String")]),this._v(" 由三部分组成,如图左侧所示:一个指向存放字符串内容内存的指针,一个长度,和一个容量。这一组数据存储在栈上。右侧则是堆上存放内容的内存部分。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("图 4-1:将值 "),s("code",[this._v('"hello"')]),this._v(" 绑定给 "),s("code",[this._v("s1")]),this._v(" 的 "),s("code",[this._v("String")]),this._v(" 在内存中的表现形式")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("长度表示 "),s("code",[this._v("String")]),this._v(" 的内容当前使用了多少字节的内存。容量是 "),s("code",[this._v("String")]),this._v(" 从操作系统总共获取了多少字节的内存。长度与容量的区别是很重要的,不过在当前上下文中并不重要,所以现在可以忽略容量。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当我们将 "),s("code",[this._v("s1")]),this._v(" 赋值给 "),s("code",[this._v("s2")]),this._v(","),s("code",[this._v("String")]),this._v(" 的数据被复制了,这意味着我们从栈上拷贝了它的指针、长度和容量。我们并没有复制指针指向的堆上数据。换句话说,内存中数据的表现如图 4-2 所示。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("图 4-2:变量 "),s("code",[this._v("s2")]),this._v(" 的内存表现,它有一份 "),s("code",[this._v("s1")]),this._v(" 指针、长度和容量的拷贝")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个表现形式看起来 "),s("strong",[this._v("并不像")]),this._v(" 图 4-3 中的那样,如果 Rust 也拷贝了堆上的数据,那么内存看起来就是这样的。如果 Rust 这么做了,那么操作 "),s("code",[this._v("s2 = s1")]),this._v(" 在堆上数据比较大的时候会对运行时性能造成非常大的影响。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("图 4-3:另一个 "),s("code",[this._v("s2 = s1")]),this._v(" 时可能的内存表现,如果 Rust 同时也拷贝了堆上的数据的话")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("之前我们提到过当变量离开作用域后,Rust 自动调用 "),n("code",[t._v("drop")]),t._v(" 函数并清理变量的堆内存。不过图 4-2 展示了两个数据指针指向了同一位置。这就有了一个问题:当 "),n("code",[t._v("s2")]),t._v(" 和 "),n("code",[t._v("s1")]),t._v(" 离开作用域,他们都会尝试释放相同的内存。这是一个叫做 "),n("strong",[t._v("二次释放")]),t._v("("),n("em",[t._v("double free")]),t._v(")的错误,也是之前提到过的内存安全性 bug 之一。两次释放(相同)内存会导致内存污染,它可能会导致潜在的安全漏洞。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("为了确保内存安全,这种场景下 Rust 的处理有另一个细节值得注意。与其尝试拷贝被分配的内存,Rust 则认为 "),n("code",[t._v("s1")]),t._v(" 不再有效,因此 Rust 不需要在 "),n("code",[t._v("s1")]),t._v(" 离开作用域后清理任何东西。看看在 "),n("code",[t._v("s2")]),t._v(" 被创建之后尝试使用 "),n("code",[t._v("s1")]),t._v(" 会发生什么;这段代码不能运行:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let s1 = String::from("hello");\nlet s2 = s1;\n\nprintln!("{}, world!", s1);\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0382]: use of moved value: `s1`\n --\x3e src/main.rs:5:28\n |\n3 | let s2 = s1;\n | -- value moved here\n4 |\n5 | println!("{}, world!", s1);\n | ^^ value used here after move\n |\n = note: move occurs because `s1` has type `std::string::String`, which does\n not implement the `Copy` trait\n')])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如果你在其他语言中听说过术语 "),n("strong",[t._v("浅拷贝")]),t._v("("),n("em",[t._v("shallow copy")]),t._v(")和 "),n("strong",[t._v("深拷贝")]),t._v("("),n("em",[t._v("deep copy")]),t._v("),那么拷贝指针、长度和容量而不拷贝数据可能听起来像浅拷贝。不过因为 Rust 同时使第一个变量无效了,这个操作被称为 "),n("strong",[t._v("移动")]),t._v("("),n("em",[t._v("move")]),t._v("),而不是浅拷贝。上面的例子可以解读为 "),n("code",[t._v("s1")]),t._v(" 被 "),n("strong",[t._v("移动")]),t._v(" 到了 "),n("code",[t._v("s2")]),t._v(" 中。那么具体发生了什么,如图 4-4 所示。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("图 4-4:"),s("code",[this._v("s1")]),this._v(" 无效之后的内存表现")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这样就解决了我们的问题!因为只有 "),s("code",[this._v("s2")]),this._v(" 是有效的,当其离开作用域,它就释放自己的内存,完毕。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("另外,这里还隐含了一个设计选择:Rust 永远也不会自动创建数据的 “深拷贝”。因此,任何 "),s("strong",[this._v("自动")]),this._v(" 的复制可以被认为对运行时性能影响较小。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"变量与数据交互的方式(二):克隆"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#变量与数据交互的方式(二):克隆","aria-hidden":"true"}},[this._v("#")]),this._v(" 变量与数据交互的方式(二):克隆")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果我们 "),s("strong",[this._v("确实")]),this._v(" 需要深度复制 "),s("code",[this._v("String")]),this._v(" 中堆上的数据,而不仅仅是栈上的数据,可以使用一个叫做 "),s("code",[this._v("clone")]),this._v(" 的通用函数。第五章会讨论方法语法,不过因为方法在很多语言中是一个常见功能,所以之前你可能已经见过了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这是一个实际使用 "),s("code",[this._v("clone")]),this._v(" 方法的例子:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s2 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("clone")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"s1 = {}, s2 = {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s1"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s2"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这段代码能正常运行,并且明确产生图 4-3 中行为,这里堆上的数据 "),s("strong",[this._v("确实")]),this._v(" 被复制了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当出现 "),s("code",[this._v("clone")]),this._v(" 调用时,你知道一些特定的代码被执行而且这些代码可能相当消耗资源。你很容易察觉到一些不寻常的事情正在发生。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"只在栈上的数据:拷贝"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#只在栈上的数据:拷贝","aria-hidden":"true"}},[this._v("#")]),this._v(" 只在栈上的数据:拷贝")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" y "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"x = {}, y = {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("但这段代码似乎与我们刚刚学到的内容相矛盾:没有调用 "),s("code",[this._v("clone")]),this._v(",不过 "),s("code",[this._v("x")]),this._v(" 依然有效且没有被移动到 "),s("code",[this._v("y")]),this._v(" 中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("原因是像整型这样的在编译时已知大小的类型被整个存储在栈上,所以拷贝其实际的值是快速的。这意味着没有理由在创建变量 "),s("code",[this._v("y")]),this._v(" 后使 "),s("code",[this._v("x")]),this._v(" 无效。换句话说,这里没有深浅拷贝的区别,所以这里调用 "),s("code",[this._v("clone")]),this._v(" 并不会与通常的浅拷贝有什么不同,我们可以不用管它。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("Rust 有一个叫做 "),n("code",[t._v("Copy")]),t._v(" trait 的特殊注解,可以用在类似整型这样的存储在栈上的类型上(第十章详细讲解 trait)。如果一个类型拥有 "),n("code",[t._v("Copy")]),t._v(" trait,一个旧的变量在将其赋值给其他变量后仍然可用。Rust 不允许自身或其任何部分实现了 "),n("code",[t._v("Drop")]),t._v(" trait 的类型使用 "),n("code",[t._v("Copy")]),t._v(" trait。如果我们对其值离开作用域时需要特殊处理的类型使用 "),n("code",[t._v("Copy")]),t._v(" 注解,将会出现一个编译时错误。要学习如何为你的类型增加 "),n("code",[t._v("Copy")]),t._v(" 注解,请阅读附录 C 中的 “可派生的 trait”。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("那么什么类型是 "),n("code",[t._v("Copy")]),t._v(" 的呢?可以查看给定类型的文档来确认,不过作为一个通用的规则,任何简单标量值的组合可以是 "),n("code",[t._v("Copy")]),t._v(" 的,不需要分配内存或某种形式资源的类型是 "),n("code",[t._v("Copy")]),t._v(" 的。如下是一些 "),n("code",[t._v("Copy")]),t._v(" 的类型:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ul",[n("li",[t._v("所有整数类型,比如 "),n("code",[t._v("u32")]),t._v("。")]),t._v(" "),n("li",[t._v("布尔类型,"),n("code",[t._v("bool")]),t._v(",它的值是 "),n("code",[t._v("true")]),t._v(" 和 "),n("code",[t._v("false")]),t._v("。")]),t._v(" "),n("li",[t._v("所有浮点数类型,比如 "),n("code",[t._v("f64")]),t._v("。")]),t._v(" "),n("li",[t._v("字符类型,"),n("code",[t._v("char")]),t._v("。")]),t._v(" "),n("li",[t._v("元组,当且仅当其包含的类型也都是 "),n("code",[t._v("Copy")]),t._v(" 的时候。比如,"),n("code",[t._v("(i32, i32)")]),t._v(" 是 "),n("code",[t._v("Copy")]),t._v(" 的,但 "),n("code",[t._v("(i32, String)")]),t._v(" 就不是。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"所有权与函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#所有权与函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 所有权与函数")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// s 进入作用域")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("takes_ownership")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// s 的值移动到函数里 ...")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// ... 所以到这里不再有效")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// x 进入作用域")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("makes_copy")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// x 应该移动函数里,")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 但 i32 是 Copy 的,所以在后面可继续使用 x")]),t._v("\n\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 所以不会有特殊操作")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("takes_ownership")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("some_string"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// some_string 进入作用域")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"{}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" some_string"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("makes_copy")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("some_integer"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// some_integer 进入作用域")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"{}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" some_integer"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 这里,some_integer 移出作用域。不会有特殊操作")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 4-3:带有所有权和作用域注释的函数")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当尝试在调用 "),n("code",[t._v("takes_ownership")]),t._v(" 后使用 "),n("code",[t._v("s")]),t._v(" 时,Rust 会抛出一个编译时错误。这些静态检查使我们免于犯错。试试在 "),n("code",[t._v("main")]),t._v(" 函数中添加使用 "),n("code",[t._v("s")]),t._v(" 和 "),n("code",[t._v("x")]),t._v(" 的代码来看看哪里能使用他们,以及所有权规则会在哪里阻止我们这么做。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"返回值与作用域"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#返回值与作用域","aria-hidden":"true"}},[this._v("#")]),this._v(" 返回值与作用域")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("gives_ownership")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// gives_ownership 将返回值")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 移给 s1")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s2 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// s2 进入作用域")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s3 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("takes_and_gives_back")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s2"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// s2 被移动到")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// takes_and_gives_back 中, ")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 它也将返回值移给 s3")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 所以什么也不会发生。s1 移出作用域并被丢弃")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("gives_ownership")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// gives_ownership 将返回值移动给")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 调用它的函数")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" some_string "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// some_string 进入作用域.")]),t._v("\n\n some_string "),n("span",{attrs:{class:"token comment"}},[t._v("// 返回 some_string 并移出给调用的函数")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token comment"}},[t._v("// takes_and_gives_back 将传入字符串并返回该值")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("takes_and_gives_back")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a_string"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// a_string 进入作用域")]),t._v("\n\n a_string "),n("span",{attrs:{class:"token comment"}},[t._v("// 返回 a_string 并移出给调用的函数")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 4-4: 转移返回值的所有权")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("变量的所有权总是遵循相同的模式:将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时,其值将通过 "),s("code",[this._v("drop")]),this._v(" 被清理掉,除非数据被移动为另一个变量所有。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s2"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" len"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("calculate_length")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s1"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("\"The length of '{}' is {}.\"")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s2"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" len"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("calculate_length")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" usize"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" length "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// len() 返回字符串的长度")]),t._v("\n\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" length"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 4-5: 返回参数的所有权")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("但是这未免有些形式主义,而且这种场景应该很常见。幸运的是,Rust 对此提供了一个功能,叫做 "),s("strong",[this._v("引用")]),this._v("("),s("em",[this._v("references")]),this._v(")。")])}],!1,null,null,null);e.options.__file="ch04-01-what-is-ownership.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/27.03216770.js b/src/.vuepress/dist/assets/js/27.03216770.js
new file mode 100644
index 0000000..3f07837
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/27.03216770.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[27],{255:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch04-02-references-and-borrowing.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch04-02-references-and-borrowing.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),n("img",{staticClass:"center",attrs:{alt:"&String s pointing at String s1",src:"img/trpl04-05.svg"}}),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),n("p",[t._v("仔细看看这个函数调用:")]),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),n("p",[t._v("如果我们尝试修改借用的变量呢?尝试示例 4-6 中的代码。剧透:这行不通!")]),t._v(" "),t._m(15),t._v(" "),t._m(16),t._m(17),t._v(" "),n("p",[t._v("这里是错误:")]),t._v(" "),t._m(18),n("p",[t._v("正如变量默认是不可变的,引用也一样。(默认)不允许修改引用的值。")]),t._v(" "),t._m(19),t._v(" "),n("p",[t._v("我们通过一个小调整就能修复示例 4-6 代码中的错误:")]),t._v(" "),t._m(20),t._v(" "),t._m(21),t._m(22),t._v(" "),n("p",[t._v("不过可变引用有一个很大的限制:在特定作用域中的特定数据有且只有一个可变引用。这些代码会失败:")]),t._v(" "),t._m(23),t._v(" "),t._m(24),n("p",[t._v("错误如下:")]),t._v(" "),t._m(25),n("p",[t._v("这个限制允许可变性,不过是以一种受限制的方式允许。新 Rustacean 们经常与此作斗争,因为大部分语言中变量任何时候都是可变的。")]),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),n("p",[t._v("数据竞争会导致未定义行为,难以在运行时追踪,并且难以诊断和修复;Rust 避免了这种情况的发生,因为它甚至不会编译存在数据竞争的代码!")]),t._v(" "),t._m(28),t._v(" "),t._m(29),n("p",[t._v("类似的规则也存在于同时使用可变与不可变引用中。这些代码会导致一个错误:")]),t._v(" "),t._m(30),n("p",[t._v("错误如下:")]),t._v(" "),t._m(31),t._m(32),t._v(" "),n("p",[t._v("尽管这些错误有时使人沮丧,但请牢记这是 Rust 编译器在提前指出一个潜在的 bug(在编译时而不是在运行时)并精准显示问题所在。这样你就不必去跟踪为何数据并不是你想象中的那样。")]),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),n("p",[t._v("让我们尝试创建一个悬垂引用,Rust 会通过一个编译时错误来避免:")]),t._v(" "),t._m(35),t._v(" "),t._m(36),n("p",[t._v("这里是错误:")]),t._v(" "),t._m(37),n("p",[t._v("错误信息引用了一个我们还未介绍的功能:生命周期(lifetimes)。第十章会详细介绍生命周期。不过,如果你不理会生命周期部分,错误信息中确实包含了为什么这段代码有问题的关键信息:")]),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),n("p",[t._v("这样就没有任何错误了。所有权被移动出去,所以没有值被释放。")]),t._v(" "),t._m(44),t._v(" "),n("p",[t._v("让我们概括一下之前对引用的讨论:")]),t._v(" "),t._m(45),t._v(" "),n("p",[t._v("接下来,我们来看看另一种不同类型的引用:slice。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"引用与借用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#引用与借用","aria-hidden":"true"}},[this._v("#")]),this._v(" 引用与借用")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 4-5 中的元组代码有这样一个问题:我们必须将 "),n("code",[t._v("String")]),t._v(" 返回给调用函数,以便在调用 "),n("code",[t._v("calculate_length")]),t._v(" 后仍能使用 "),n("code",[t._v("String")]),t._v(",因为 "),n("code",[t._v("String")]),t._v(" 被移动到了 "),n("code",[t._v("calculate_length")]),t._v(" 内。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("下面是如何定义并使用一个(新的)"),s("code",[this._v("calculate_length")]),this._v(" 函数,它以一个对象的引用作为参数而不是获取值的所有权:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" len "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("calculate_length")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s1"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("\"The length of '{}' is {}.\"")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s1"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" len"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("calculate_length")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" usize "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n s"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("首先,注意变量声明和函数返回值中的所有元组代码都消失了。其次,注意我们传递 "),n("code",[t._v("&s1")]),t._v(" 给 "),n("code",[t._v("calculate_length")]),t._v(",同时在函数定义中,我们获取 "),n("code",[t._v("&String")]),t._v(" 而不是 "),n("code",[t._v("String")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这些 & 符号就是 "),s("strong",[this._v("引用")]),this._v(",它们允许你使用值但不获取其所有权。图 4-5 展示了一张示意图。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("图 4-5:"),s("code",[this._v("&String s")]),this._v(" 指向 "),s("code",[this._v("String s1")]),this._v(" 示意图")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("blockquote",[n("p",[t._v("注意:与使用 "),n("code",[t._v("&")]),t._v(" 引用相反的操作是 "),n("strong",[t._v("解引用")]),t._v("("),n("em",[t._v("dereferencing")]),t._v("),它使用解引用运算符,"),n("code",[t._v("*")]),t._v("。我们将会在第八章遇到一些解引用运算符,并在第十五章详细讨论解引用。")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("calculate_length")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" usize "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# s"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" len "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("calculate_length")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s1"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("&s1")]),this._v(" 语法让我们创建一个 "),s("strong",[this._v("指向")]),this._v(" 值 "),s("code",[this._v("s1")]),this._v(" 的引用,但是并不拥有它。因为并不拥有这个值,当引用离开作用域时其指向的值也不会被丢弃。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("同理,函数签名使用 "),s("code",[this._v("&")]),this._v(" 来表明参数 "),s("code",[this._v("s")]),this._v(" 的类型是一个引用。让我们增加一些解释性的注释:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("calculate_length")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" usize "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// s 是对 String 的引用")]),t._v("\n s"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 这里,s 离开了作用域。但因为它并不拥有引用值的所有权,")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 所以什么也不会发生")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("变量 "),s("code",[this._v("s")]),this._v(" 有效的作用域与函数参数的作用域一样,不过当引用离开作用域后并不丢弃它指向的数据,因为我们没有所有权。当函数使用引用而不是实际值作为参数,无需返回值来交还所有权,因为就不曾拥有所有权。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们将获取引用作为函数参数称为 "),s("strong",[this._v("借用")]),this._v("("),s("em",[this._v("borrowing")]),this._v(")。正如现实生活中,如果一个人拥有某样东西,你可以从他那里借来。当你使用完毕,必须还回去。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let s = String::from("hello");\n\n change(&s);\n}\n\nfn change(some_string: &String) {\n some_string.push_str(", world");\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 4-6:尝试修改借用的值")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0596]: cannot borrow immutable borrowed content `*some_string` as mutable\n --\x3e error.rs:8:5\n |\n7 | fn change(some_string: &String) {\n | ------- use `&mut String` here to make mutable\n8 | some_string.push_str(", world");\n | ^^^^^^^^^^^ cannot borrow as mutable\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"可变引用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#可变引用","aria-hidden":"true"}},[this._v("#")]),this._v(" 可变引用")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("change")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("change")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("some_string"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n some_string"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("push_str")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('", world"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("首先,必须将 "),n("code",[t._v("s")]),t._v(" 改为 "),n("code",[t._v("mut")]),t._v("。然后必须创建一个可变引用 "),n("code",[t._v("&mut s")]),t._v(" 和接受一个可变引用 "),n("code",[t._v("some_string: &mut String")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let mut s = String::from("hello");\n\nlet r1 = &mut s;\nlet r2 = &mut s;\n\nprintln!("{}, {}", r1, r2);\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0499]: cannot borrow `s` as mutable more than once at a time\n --\x3e src/main.rs:5:10\n |\n4 | let r1 = &mut s;\n | ------ first mutable borrow occurs here\n5 | let r2 = &mut s;\n | ^^^^^^ second mutable borrow occurs here\n6 | println!("{}, {}", r1, r2);\n | -- borrow later used here\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个限制的好处是 Rust 可以在编译时就避免数据竞争。"),s("strong",[this._v("数据竞争")]),this._v("("),s("em",[this._v("data race")]),this._v(")类似于竞态条件,它可由这三个行为造成:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[this._v("两个或更多指针同时访问同一数据。")]),this._v(" "),s("li",[this._v("至少有一个指针被用来写入数据。")]),this._v(" "),s("li",[this._v("没有同步数据访问的机制。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("一如既往,可以使用大括号来创建一个新的作用域,以允许拥有多个可变引用,只是不能 "),s("strong",[this._v("同时")]),this._v(" 拥有:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" r1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// r1 在这里离开了作用域,所以我们完全可以创建一个新的引用")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" r2 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let mut s = String::from("hello");\n\nlet r1 = &s; // no problem\nlet r2 = &s; // no problem\nlet r3 = &mut s; // BIG PROBLEM\n\nprintln!("{}, {}, and {}", r1, r2, r3);\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable\n --\x3e src/main.rs:6:10\n |\n4 | let r1 = &s; // no problem\n | -- immutable borrow occurs here\n5 | let r2 = &s; // no problem\n6 | let r3 = &mut s; // BIG PROBLEM\n | ^^^^^^ mutable borrow occurs here\n7 |\n8 | println!("{}, {}, and {}", r1, r2, r3);\n | -- borrow later used here\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("哇哦!我们 "),s("strong",[this._v("也")]),this._v(" 不能在拥有不可变引用的同时拥有可变引用。不可变引用的用户可不希望在他们的眼皮底下值就被意外的改变了!然而,多个不可变引用是可以的,因为没有哪个只能读取数据的人有能力影响其他人读取到的数据。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"悬垂引用(dangling-references)"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#悬垂引用(dangling-references)","aria-hidden":"true"}},[this._v("#")]),this._v(" 悬垂引用(Dangling References)")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 "),s("strong",[this._v("悬垂指针")]),this._v("("),s("em",[this._v("dangling pointer")]),this._v("),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let reference_to_nothing = dangle();\n}\n\nfn dangle() -> &String {\n let s = String::from("hello");\n\n &s\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0106]: missing lifetime specifier\n --\x3e main.rs:5:16\n |\n5 | fn dangle() -> &String {\n | ^ expected lifetime parameter\n |\n = help: this function's return type contains a borrowed value, but there is\n no value for it to be borrowed from\n = help: consider giving it a 'static lifetime\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("this function's return type contains a borrowed value, but there is no value\nfor it to be borrowed from.\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们仔细看看我们的 "),s("code",[this._v("dangle")]),this._v(" 代码的每一步到底发生了什么:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn dangle() -> &String { // dangle 返回一个字符串的引用\n\n let s = String::from("hello"); // s 是一个新字符串\n\n &s // 返回字符串 s 的引用\n} // 这里 s 离开作用域并被丢弃。其内存被释放。\n // 危险!\n')])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("因为 "),n("code",[t._v("s")]),t._v(" 是在 "),n("code",[t._v("dangle")]),t._v(" 函数内创建的,当 "),n("code",[t._v("dangle")]),t._v(" 的代码执行完毕后,"),n("code",[t._v("s")]),t._v(" 将被释放。不过我们尝试返回它的引用。这意味着这个引用会指向一个无效的 "),n("code",[t._v("String")]),t._v(",这可不对!Rust 不会允许我们这么做。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里的解决方法是直接返回 "),s("code",[this._v("String")]),this._v(":")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("no_dangle")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n s\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"引用的规则"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#引用的规则","aria-hidden":"true"}},[this._v("#")]),this._v(" 引用的规则")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[this._v("在任意给定时间,"),s("strong",[this._v("要么")]),this._v(" 只能有一个可变引用,"),s("strong",[this._v("要么")]),this._v(" 只能有多个不可变引用。")]),this._v(" "),s("li",[this._v("引用必须总是有效。")])])}],!1,null,null,null);e.options.__file="ch04-02-references-and-borrowing.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/28.82b934bf.js b/src/.vuepress/dist/assets/js/28.82b934bf.js
new file mode 100644
index 0000000..d1ee609
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/28.82b934bf.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[28],{253:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch04-03-slices.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch04-03-slices.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),a("p",[t._v("这里有一个编程小习题:编写一个函数,该函数接收一个字符串,并返回在该字符串中找到的第一个单词。如果函数在该字符串中并未找到空格,则整个字符串就是一个单词,所以应该返回整个字符串。")]),t._v(" "),a("p",[t._v("让我们考虑一下这个函数的签名:")]),t._v(" "),t._m(2),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._m(22),t._v(" "),a("p",[t._v("幸运的是,Rust 为这个问题提供了一个解决方法:字符串 slice。")]),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._m(28),t._v(" "),t._m(29),t._v(" "),a("p",[t._v("图 4-6 展示了一个图例。")]),t._v(" "),a("img",{staticClass:"center",staticStyle:{width:"50%"},attrs:{alt:"world containing a pointer to the 6th byte of String s and a length 5",src:"img/trpl04-06.svg"}}),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),a("p",[t._v("也可以同时舍弃这两个值来获取整个字符串的 slice。所以如下亦是相同的:")]),t._v(" "),t._m(35),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),a("p",[t._v("我们使用跟示例 4-7 相同的方式获取单词结尾的索引,通过寻找第一个出现的空格。当找到一个空格,我们返回一个字符串 slice,它使用字符串的开始和空格的索引作为开始和结束的索引。")]),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),a("p",[t._v("这里是编译错误:")]),t._v(" "),t._m(46),t._m(47),t._v(" "),t._m(48),t._v(" "),a("p",[t._v("还记得我们讲到过字符串字面值被储存在二进制文件中吗。现在知道 slice 了,我们就可以正确的理解字符串字面值了:")]),t._v(" "),t._m(49),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),t._m(54),t._v(" "),t._m(55),t._m(56),t._v(" "),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._m(60),t._v(" "),a("p",[t._v("字符串 slice,正如你想象的那样,是针对字符串的。不过也有更通用的 slice 类型。考虑一下这个数组:")]),t._v(" "),t._m(61),a("p",[t._v("就跟我们想要获取字符串的一部分那样,我们也会想要引用数组的一部分。我们可以这样做:")]),t._v(" "),t._m(62),t._m(63),t._v(" "),t._m(64),t._v(" "),a("p",[t._v("所有权、借用和 slice 这些概念让 Rust 程序在编译时确保内存安全。Rust 语言提供了跟其他系统编程语言相同的方式来控制你使用的内存,但拥有数据所有者在离开作用域后自动清除其数据的功能意味着你无须额外编写和调试相关的控制代码。")]),t._v(" "),t._m(65)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"slice-类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#slice-类型","aria-hidden":"true"}},[this._v("#")]),this._v(" Slice 类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("另一个没有所有权的数据类型是 "),s("em",[this._v("slice")]),this._v("。slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn first_word(s: &String) -> ?\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("first_word")]),this._v(" 函数有一个参数 "),s("code",[this._v("&String")]),this._v("。因为我们不需要所有权,所以这没有问题。不过应该返回什么呢?我们并没有一个真正获取 "),s("strong",[this._v("部分")]),this._v(" 字符串的办法。不过,我们可以返回单词结尾的索引。试试如示例 4-7 中的代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("first_word")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" usize "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" bytes "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("as_bytes")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("item"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" bytes"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("iter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("enumerate")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" item "),a("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{attrs:{class:"token char string"}},[t._v("b' '")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" i"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n s"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("len")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 4-7:"),s("code",[this._v("first_word")]),this._v(" 函数返回 "),s("code",[this._v("String")]),this._v(" 参数的一个字节索引值")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为需要逐个元素的检查 "),s("code",[this._v("String")]),this._v(" 中的值是否为空格,需要用 "),s("code",[this._v("as_bytes")]),this._v(" 方法将 "),s("code",[this._v("String")]),this._v(" 转化为字节数组:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("let bytes = s.as_bytes();\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接下来,使用 "),s("code",[this._v("iter")]),this._v(" 方法在字节数组上创建一个迭代器:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("for (i, &item) in bytes.iter().enumerate() {\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("我们将在第十三章详细讨论迭代器。现在,只需知道 "),a("code",[t._v("iter")]),t._v(" 方法返回集合中的每一个元素,而 "),a("code",[t._v("enumerate")]),t._v(" 包装 "),a("code",[t._v("iter")]),t._v(" 的结果并返回一个元组,其中每一个元素是元组的一部分。"),a("code",[t._v("enumerate")]),t._v(" 返回元组的第一个元素是索引,第二个元素是集合中元素的引用。这比我们自己计算索引要方便一些。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("因为 "),a("code",[t._v("enumerate")]),t._v(" 方法返回一个元组,我们可以使用模式来解构,就像 Rust 中其他任何地方所做的一样。所以在 "),a("code",[t._v("for")]),t._v(" 循环中,我们指定了一个模式,其中元组中的 "),a("code",[t._v("i")]),t._v(" 是索引而元组中的 "),a("code",[t._v("&item")]),t._v(" 是单个字节。因为我们从 "),a("code",[t._v(".iter().enumerate()")]),t._v(" 中获取了集合元素的引用,所以模式中使用了 "),a("code",[t._v("&")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 "),s("code",[this._v("for")]),this._v(" 循环中,我们通过字节的字面值语法来寻找代表空格的字节。如果找到了一个空格,返回它的位置。否则,使用 "),s("code",[this._v("s.len()")]),this._v(" 返回字符串的长度:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v(" if item == b' ' {\n return i;\n }\n}\n\ns.len()\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("现在有了一个找到字符串中第一个单词结尾索引的方法,不过这有一个问题。我们返回了一个独立的 "),a("code",[t._v("usize")]),t._v(",不过它只在 "),a("code",[t._v("&String")]),t._v(" 的上下文中才是一个有意义的数字。换句话说,因为它是一个与 "),a("code",[t._v("String")]),t._v(" 相分离的值,无法保证将来它仍然有效。考虑一下示例 4-8 中使用了示例 4-7 中 "),a("code",[t._v("first_word")]),t._v(" 函数的程序。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("first_word")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" usize "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" bytes "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("as_bytes")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("item"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" bytes"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("iter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("enumerate")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" item "),a("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{attrs:{class:"token char string"}},[t._v("b' '")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" i"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# s"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("len")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"hello world"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" word "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("first_word")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// word 的值为 5")]),t._v("\n\n s"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("clear")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v('// 这清空了字符串,使其等于 ""')]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// word 在此处的值仍然是 5,")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// 但是没有更多的字符串让我们可以有效地应用数值 5。word 的值现在完全无效!")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 4-8:存储 "),s("code",[this._v("first_word")]),this._v(" 函数调用的返回值并接着改变 "),s("code",[this._v("String")]),this._v(" 的内容")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这个程序编译时没有任何错误,而且在调用 "),a("code",[t._v("s.clear()")]),t._v(" 之后使用 "),a("code",[t._v("word")]),t._v(" 也不会出错。因为 "),a("code",[t._v("word")]),t._v(" 与 "),a("code",[t._v("s")]),t._v(" 状态完全没有联系,所以 "),a("code",[t._v("word")]),t._v(" 仍然包含值 "),a("code",[t._v("5")]),t._v("。可以尝试用值 "),a("code",[t._v("5")]),t._v(" 来提取变量 "),a("code",[t._v("s")]),t._v(" 的第一个单词,不过这是有 bug 的,因为在我们将 "),a("code",[t._v("5")]),t._v(" 保存到 "),a("code",[t._v("word")]),t._v(" 之后 "),a("code",[t._v("s")]),t._v(" 的内容已经改变。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们不得不时刻担心 "),s("code",[this._v("word")]),this._v(" 的索引与 "),s("code",[this._v("s")]),this._v(" 中的数据不再同步,这很啰嗦且易出错!如果编写这么一个 "),s("code",[this._v("second_word")]),this._v(" 函数的话,管理索引这件事将更加容易出问题。它的签名看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn second_word(s: &String) -> (usize, usize) {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在我们要跟踪一个开始索引 "),s("strong",[this._v("和")]),this._v(" 一个结尾索引,同时有了更多从数据的某个特定状态计算而来的值,但都完全没有与这个状态相关联。现在有三个飘忽不定的不相关变量需要保持同步。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"字符串-slice"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#字符串-slice","aria-hidden":"true"}},[this._v("#")]),this._v(" 字符串 slice")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("字符串 slice")]),this._v("("),s("em",[this._v("string slice")]),this._v(")是 "),s("code",[this._v("String")]),this._v(" 中一部分值的引用,它看起来像这样:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"hello world"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" world "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("6")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token number"}},[t._v("11")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这类似于引用整个 "),a("code",[t._v("String")]),t._v(" 不过带有额外的 "),a("code",[t._v("[0..5]")]),t._v(" 部分。它不是对整个 "),a("code",[t._v("String")]),t._v(" 的引用,而是对部分 "),a("code",[t._v("String")]),t._v(" 的引用。"),a("code",[t._v("start..end")]),t._v(" 语法代表一个以 "),a("code",[t._v("start")]),t._v(" 开头并一直持续到但不包含 "),a("code",[t._v("end")]),t._v(" 的 range。如果需要包含 "),a("code",[t._v("end")]),t._v(",可以使用 "),a("code",[t._v("..=")]),t._v(" 而不是 "),a("code",[t._v("..")]),t._v(":")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"hello world"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token operator"}},[t._v("=")]),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" world "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("6")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token operator"}},[t._v("=")]),a("span",{attrs:{class:"token number"}},[t._v("10")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("=")]),this._v(" 意味着包含最后的数字,这有助于你记住 "),s("code",[this._v("..")]),this._v(" 与 "),s("code",[this._v("..=")]),this._v(" 的区别")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("可以使用一个由中括号中的 "),a("code",[t._v("[starting_index..ending_index]")]),t._v(" 指定的 range 创建一个 slice,其中 "),a("code",[t._v("starting_index")]),t._v(" 是 slice 的第一个位置,"),a("code",[t._v("ending_index")]),t._v(" 则是 slice 最后一个位置的后一个值。在其内部,slice 的数据结构存储了 slice 的开始位置和长度,长度对应于 "),a("code",[t._v("ending_index")]),t._v(" 减去 "),a("code",[t._v("starting_index")]),t._v(" 的值。所以对于 "),a("code",[t._v("let world = &s[6..11];")]),t._v(" 的情况,"),a("code",[t._v("world")]),t._v(" 将是一个包含指向 "),a("code",[t._v("s")]),t._v(" 第 7 个字节的指针和长度值 5 的 slice。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("图 4-6:引用了部分 "),s("code",[this._v("String")]),this._v(" 的字符串 slice")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("对于 Rust 的 "),s("code",[this._v("..")]),this._v(" range 语法,如果想要从第一个索引(0)开始,可以不写两个点号之前的值。换句话说,如下两个语句是相同的:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" slice "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" slice "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("由此类推,如果 slice 包含 "),s("code",[this._v("String")]),this._v(" 的最后一个字节,也可以舍弃尾部的数字。这意味着如下也是相同的:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" len "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("len")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" slice "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("len"),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" slice "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" len "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("len")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" slice "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("len"),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" slice "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("blockquote",[s("p",[this._v("注意:字符串 slice range 的索引必须位于有效的 UTF-8 字符边界内,如果尝试从一个多字节字符的中间位置创建字符串 slice,则程序将会因错误而退出。出于介绍字符串 slice 的目的,本部分假设只使用 ASCII 字符集;第八章的 “使用字符串存储 UTF-8 编码的文本” 部分会更加全面的讨论 UTF-8 处理问题。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在记住所有这些知识后,让我们重写 "),s("code",[this._v("first_word")]),this._v(" 来返回一个 slice。“字符串 slice” 的类型声明写作 "),s("code",[this._v("&str")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("first_word")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" bytes "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("as_bytes")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("item"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" bytes"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("iter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("enumerate")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" item "),a("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{attrs:{class:"token char string"}},[t._v("b' '")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("i"),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在当调用 "),s("code",[this._v("first_word")]),this._v(" 时,会返回与底层数据关联的单个值。这个值由一个 slice 开始位置的引用和 slice 中元素的数量组成。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("second_word")]),this._v(" 函数也可以改为返回一个 slice:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn second_word(s: &String) -> &str {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在我们有了一个不易混淆且直观的 API 了,因为编译器会确保指向 "),s("code",[this._v("String")]),this._v(" 的引用持续有效。还记得示例 4-8 程序中,那个当我们获取第一个单词结尾的索引后,接着就清除了字符串导致索引就无效的 bug 吗?那些代码在逻辑上是不正确的,但却没有显示任何直接的错误。问题会在之后尝试对空字符串使用第一个单词的索引时出现。slice 就不可能出现这种 bug 并让我们更早的知道出问题了。使用 slice 版本的 "),s("code",[this._v("first_word")]),this._v(" 会抛出一个编译时错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let mut s = String::from("hello world");\n\n let word = first_word(&s);\n\n s.clear(); // error!\n\n println!("the first word is: {}", word);\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable\n --\x3e src/main.rs:10:5\n |\n8 | let word = first_word(&s);\n | -- immutable borrow occurs here\n9 |\n10 | s.clear(); // error!\n | ^^^^^^^^^ mutable borrow occurs here\n11 |\n12 | println!("the first word is: {}", word);\n | ---- borrow later used here\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("回忆一下借用规则,当拥有某值的不可变引用时,就不能再获取一个可变引用。因为 "),s("code",[this._v("clear")]),this._v(" 需要清空 "),s("code",[this._v("String")]),this._v(",它尝试获取一个可变引用,它失败了。Rust 不仅使得我们的 API 简单易用,也在编译时就消除了一整类的错误!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"字符串字面值就是-slice"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#字符串字面值就是-slice","aria-hidden":"true"}},[this._v("#")]),this._v(" 字符串字面值就是 slice")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[this._v("let")]),this._v(" s "),s("span",{attrs:{class:"token operator"}},[this._v("=")]),this._v(" "),s("span",{attrs:{class:"token string"}},[this._v('"Hello, world!"')]),s("span",{attrs:{class:"token punctuation"}},[this._v(";")]),this._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里 "),s("code",[this._v("s")]),this._v(" 的类型是 "),s("code",[this._v("&str")]),this._v(":它是一个指向二进制程序特定位置的 slice。这也就是为什么字符串字面值是不可变的;"),s("code",[this._v("&str")]),this._v(" 是一个不可变引用。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"字符串-slice-作为参数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#字符串-slice-作为参数","aria-hidden":"true"}},[this._v("#")]),this._v(" 字符串 slice 作为参数")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在知道了能够获取字面值和 "),s("code",[this._v("String")]),this._v(" 的 slice 后,我们对 "),s("code",[this._v("first_word")]),this._v(" 做了改进,这是它的签名:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn first_word(s: &String) -> &str {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("而更有经验的 Rustacean 会编写出示例 4-9 中的签名,因为它使得可以对 "),s("code",[this._v("String")]),this._v(" 值和 "),s("code",[this._v("&str")]),this._v(" 值使用相同的函数:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn first_word(s: &str) -> &str {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 4-9: 通过将 "),s("code",[this._v("s")]),this._v(" 参数的类型改为字符串 slice 来改进 "),s("code",[this._v("first_word")]),this._v(" 函数")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果有一个字符串 slice,可以直接传递它。如果有一个 "),s("code",[this._v("String")]),this._v(",则可以传递整个 "),s("code",[this._v("String")]),this._v(" 的 slice。定义一个获取字符串 slice 而不是 "),s("code",[this._v("String")]),this._v(" 引用的函数使得我们的 API 更加通用并且不会丢失任何功能:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("first_word")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" bytes "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("as_bytes")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("item"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" bytes"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("iter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("enumerate")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" item "),a("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{attrs:{class:"token char string"}},[t._v("b' '")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("i"),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" my_string "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"hello world"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// first_word 中传入 `String` 的 slice")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" word "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("first_word")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("my_string"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" my_string_literal "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"hello world"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// first_word 中传入字符串字面值的 slice")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" word "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("first_word")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("my_string_literal"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// 因为字符串字面值 **就是** 字符串 slice,")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// 这样写也可以,即不使用 slice 语法!")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" word "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("first_word")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("my_string_literal"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"其他类型的-slice"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#其他类型的-slice","aria-hidden":"true"}},[this._v("#")]),this._v(" 其他类型的 slice")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" a "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" a "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" slice "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("a"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个 slice 的类型是 "),s("code",[this._v("&[i32]")]),this._v("。它跟字符串 slice 的工作方式一样,通过存储第一个集合元素的引用和一个集合总长度。你可以对其他所有集合使用这类 slice。第八章讲到 vector 时会详细讨论这些集合。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("所有权系统影响了 Rust 中很多其他部分的工作方式,所以我们还会继续讲到这些概念,这将贯穿本书的余下内容。让我们开始第五章,来看看如何将多份数据组合进一个 "),s("code",[this._v("struct")]),this._v(" 中。")])}],!1,null,null,null);e.options.__file="ch04-03-slices.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/29.9bbe7e55.js b/src/.vuepress/dist/assets/js/29.9bbe7e55.js
new file mode 100644
index 0000000..7a0f14d
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/29.9bbe7e55.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{252:function(t,e,s){"use strict";s.r(e);var r=s(0),c=Object(r.a)({},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[this._m(0),this._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch05-00-structs.md",target:"_blank",rel:"noopener noreferrer"}},[this._v("ch05-00-structs.md"),e("OutboundLink")],1),this._v(" "),e("br"),this._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),this._v(" "),this._m(1)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"使用结构体组织相关联的数据"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#使用结构体组织相关联的数据","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用结构体组织相关联的数据")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[s("em",[t._v("struct")]),t._v(",或者 "),s("em",[t._v("structure")]),t._v(",是一个自定义数据类型,允许你命名和包装多个相关的值,从而形成一个有意义的组合。如果你熟悉一门面向对象语言,"),s("em",[t._v("struct")]),t._v(" 就像对象中的数据属性。在本章中,我们会对比元组与结构体的异同,演示结构体的用法,并讨论如何在结构体上定义方法和关联函数来指定与结构体数据相关的行为。你在程序中基于结构体和枚举("),s("em",[t._v("enum")]),t._v(")(在第六章介绍)创建新类型,以充分利用 Rust 的编译时类型检查。")])}],!1,null,null,null);c.options.__file="ch05-00-structs.md";e.default=c.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/3.ba62e17c.js b/src/.vuepress/dist/assets/js/3.ba62e17c.js
new file mode 100644
index 0000000..bf1612b
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/3.ba62e17c.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[3],{248:function(t,s,e){"use strict";e.r(s);var n=e(0),i=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"content"},[s("h1",{attrs:{id:"rust-程序设计语言(第二版)-简体中文版"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#rust-程序设计语言(第二版)-简体中文版","aria-hidden":"true"}},[this._v("#")]),this._v(" Rust 程序设计语言(第二版) 简体中文版")])])}],!1,null,null,null);i.options.__file="PREFACE.md";s.default=i.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/30.ed75778f.js b/src/.vuepress/dist/assets/js/30.ed75778f.js
new file mode 100644
index 0000000..a80d432
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/30.ed75778f.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[30],{250:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch05-01-defining-structs.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch05-01-defining-structs.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),n("p",[t._v("结构体和我们在第三章讨论过的元组类似。和元组一样,结构体的每一部分可以是不同类型。但不同于元组,结构体需要命名各部分数据以便能清楚的表明其值的意义。由于有了这些名字,结构体比元组更灵活:不需要依赖顺序来指定或访问实例中的值。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),n("p",[t._v("注意整个实例必须是可变的;Rust 并不允许只将某个字段标记为可变。另外需要注意同其他任何表达式一样,我们可以在函数体的最后一个表达式中构造一个结构体的新实例,来隐式地返回这个实例。")]),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"定义并实例化结构体"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#定义并实例化结构体","aria-hidden":"true"}},[this._v("#")]),this._v(" 定义并实例化结构体")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("定义结构体,需要使用 "),s("code",[this._v("struct")]),this._v(" 关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。接着,在大括号中,定义每一部分数据的名字和类型,我们称为 "),s("strong",[this._v("字段")]),this._v("("),s("em",[this._v("field")]),this._v(")。例如,示例 5-1 展示了一个存储用户账号信息的结构体:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u64"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-1:"),s("code",[this._v("User")]),this._v(" 结构体定义")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("一旦定义了结构体后,为了使用它,通过为每个字段指定具体值来创建这个结构体的 "),s("strong",[this._v("实例")]),this._v("。创建一个实例需要以结构体的名字开头,接着在大括号中使用 "),s("code",[this._v("key: value")]),this._v(" 键-值对的形式提供字段,其中 key 是字段的名字,value 是需要存储在字段中的数据值。实例中字段的顺序不需要和它们在结构体中声明的顺序一致。换句话说,结构体的定义就像一个类型的通用模板,而实例则会在这个模板中放入特定数据来创建这个类型的值。例如,可以像示例 5-2 这样来声明一个特定的用户:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u64"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" user1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"someone@example.com"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"someusername123"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("true")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-2:创建 "),s("code",[this._v("User")]),this._v(" 结构体的实例")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为了从结构体中获取某个特定的值,可以使用点号。如果我们只想要用户的邮箱地址,可以用 "),s("code",[this._v("user1.email")]),this._v("。要更改结构体中的值,如果结构体的实例是可变的,我们可以使用点号并为对应的字段赋值。示例 5-3 展示了如何改变一个可变的 "),s("code",[this._v("User")]),this._v(" 实例 "),s("code",[this._v("email")]),this._v(" 字段的值:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u64"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" user1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"someone@example.com"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"someusername123"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("true")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nuser1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("email "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"anotheremail@example.com"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-3:改变 "),s("code",[this._v("User")]),this._v(" 实例 "),s("code",[this._v("email")]),this._v(" 字段的值")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 5-4 显示了一个 "),n("code",[t._v("build_user")]),t._v(" 函数,它返回一个带有给定的 email 和用户名的 "),n("code",[t._v("User")]),t._v(" 结构体实例。"),n("code",[t._v("active")]),t._v(" 字段的值为 "),n("code",[t._v("true")]),t._v(",并且 "),n("code",[t._v("sign_in_count")]),t._v(" 的值为 "),n("code",[t._v("1")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u64"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("build_user")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" email"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" username"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("true")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-4:"),s("code",[this._v("build_user")]),this._v(" 函数获取 email 和用户名并返回 "),s("code",[this._v("User")]),this._v(" 实例")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为函数参数起与结构体字段相同的名字是可以理解的,但是不得不重复 "),s("code",[this._v("email")]),this._v(" 和 "),s("code",[this._v("username")]),this._v(" 字段名称与变量有些啰嗦。如果结构体有更多字段,重复每个名称就更加烦人了。幸运的是,有一个方便的简写语法!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"变量与字段同名时的字段初始化简写语法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#变量与字段同名时的字段初始化简写语法","aria-hidden":"true"}},[this._v("#")]),this._v(" 变量与字段同名时的字段初始化简写语法")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("因为示例 5-4 中的参数名与字段名都完全相同,我们可以使用 "),n("strong",[t._v("字段初始化简写语法")]),t._v("("),n("em",[t._v("field init shorthand")]),t._v(")来重写 "),n("code",[t._v("build_user")]),t._v(",这样其行为与之前完全相同,不过无需重复 "),n("code",[t._v("email")]),t._v(" 和 "),n("code",[t._v("username")]),t._v(" 了,如示例 5-5 所示。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u64"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("build_user")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n email"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n username"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("true")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-5:"),s("code",[this._v("build_user")]),this._v(" 函数使用了字段初始化简写语法,因为 "),s("code",[this._v("email")]),this._v(" 和 "),s("code",[this._v("username")]),this._v(" 参数与结构体字段同名")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里我们创建了一个新的 "),n("code",[t._v("User")]),t._v(" 结构体实例,它有一个叫做 "),n("code",[t._v("email")]),t._v(" 的字段。我们想要将 "),n("code",[t._v("email")]),t._v(" 字段的值设置为 "),n("code",[t._v("build_user")]),t._v(" 函数 "),n("code",[t._v("email")]),t._v(" 参数的值。因为 "),n("code",[t._v("email")]),t._v(" 字段与 "),n("code",[t._v("email")]),t._v(" 参数有着相同的名称,则只需编写 "),n("code",[t._v("email")]),t._v(" 而不是 "),n("code",[t._v("email: email")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用结构体更新语法从其他实例创建实例"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用结构体更新语法从其他实例创建实例","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用结构体更新语法从其他实例创建实例")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用旧实例的大部分值但改变其部分值来创建一个新的结构体实例通常是很有帮助的。这可以通过 "),s("strong",[this._v("结构体更新语法")]),this._v("("),s("em",[this._v("struct update syntax")]),this._v(")实现。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("首先,示例 5-6 展示了不使用更新语法时,如何在 "),n("code",[t._v("user2")]),t._v(" 中创建一个新 "),n("code",[t._v("User")]),t._v(" 实例。我们为 "),n("code",[t._v("email")]),t._v(" 和 "),n("code",[t._v("username")]),t._v(" 设置了新的值,其他值则使用了实例 5-2 中创建的 "),n("code",[t._v("user1")]),t._v(" 中的同名值:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u64"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" user1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"someone@example.com"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"someusername123"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("true")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" user2 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"another@example.com"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"anotherusername567"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" user1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("active"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" user1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-6:创建 "),s("code",[this._v("User")]),this._v(" 新实例,其使用了一些来自 "),s("code",[this._v("user1")]),this._v(" 的值")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用结构体更新语法,我们可以通过更少的代码来达到相同的效果,如示例 5-7 所示。"),s("code",[this._v("..")]),this._v(" 语法指定了剩余未显式设置值的字段应有与给定实例对应字段相同的值。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u64"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" user1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"someone@example.com"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"someusername123"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# active"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("true")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# sign_in_count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" user2 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" User "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n email"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"another@example.com"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n username"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"anotherusername567"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("user1\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("span",{staticClass:"caption"},[t._v("示例 5-7:使用结构体更新语法为一个 "),n("code",[t._v("User")]),t._v(" 实例设置新的 "),n("code",[t._v("email")]),t._v(" 和 "),n("code",[t._v("username")]),t._v(" 值,不过其余值来自 "),n("code",[t._v("user1")]),t._v(" 变量中实例的字段")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 5-7 中的代码也在 "),n("code",[t._v("user2")]),t._v(" 中创建了一个新实例,其有不同的 "),n("code",[t._v("email")]),t._v(" 和 "),n("code",[t._v("username")]),t._v(" 值不过 "),n("code",[t._v("active")]),t._v(" 和 "),n("code",[t._v("sign_in_count")]),t._v(" 字段的值与 "),n("code",[t._v("user1")]),t._v(" 相同。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用没有命名字段的元组结构体来创建不同的类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用没有命名字段的元组结构体来创建不同的类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用没有命名字段的元组结构体来创建不同的类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("也可以定义与元组(在第三章讨论过)类似的结构体,称为 "),s("strong",[this._v("元组结构体")]),this._v("("),s("em",[this._v("tuple structs")]),this._v(")。元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。当你想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,元组结构体是很有用的,这时像常规结构体那样为每个字段命名就显得多余和形式化了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("定义元组结构体,以 "),s("code",[this._v("struct")]),this._v(" 关键字和结构体名开头并后跟元组中的类型。例如,下面是两个分别叫做 "),s("code",[this._v("Color")]),this._v(" 和 "),s("code",[this._v("Point")]),this._v(" 元组结构体的定义和用法:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Color")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Point")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" black "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Color")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" origin "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Point")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意 "),n("code",[t._v("black")]),t._v(" 和 "),n("code",[t._v("origin")]),t._v(" 值的类型不同,因为它们是不同的元组结构体的实例。你定义的每一个结构体有其自己的类型,即使结构体中的字段有着相同的类型。例如,一个获取 "),n("code",[t._v("Color")]),t._v(" 类型参数的函数不能接受 "),n("code",[t._v("Point")]),t._v(" 作为参数,即便这两个类型都由三个 "),n("code",[t._v("i32")]),t._v(" 值组成。在其他方面,元组结构体实例类似于元组:可以将其解构为单独的部分,也可以使用 "),n("code",[t._v(".")]),t._v(" 后跟索引来访问单独的值,等等。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"没有任何字段的类单元结构体"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#没有任何字段的类单元结构体","aria-hidden":"true"}},[this._v("#")]),this._v(" 没有任何字段的类单元结构体")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们也可以定义一个没有任何字段的结构体!它们被称为 "),s("strong",[this._v("类单元结构体")]),this._v("("),s("em",[this._v("unit-like structs")]),this._v(")因为它们类似于 "),s("code",[this._v("()")]),this._v(",即 unit 类型。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。我们将在第十章介绍 trait。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("blockquote",[n("h3",{attrs:{id:"结构体数据的所有权"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#结构体数据的所有权","aria-hidden":"true"}},[t._v("#")]),t._v(" 结构体数据的所有权")]),t._v(" "),n("p",[t._v("在示例 5-1 中的 "),n("code",[t._v("User")]),t._v(" 结构体的定义中,我们使用了自身拥有所有权的 "),n("code",[t._v("String")]),t._v(" 类型而不是 "),n("code",[t._v("&str")]),t._v(" 字符串 slice 类型。这是一个有意而为之的选择,因为我们想要这个结构体拥有它所有的数据,为此只要整个结构体是有效的话其数据也是有效的。")]),t._v(" "),n("p",[t._v("可以使结构体存储被其他对象拥有的数据的引用,不过这么做的话需要用上 "),n("strong",[t._v("生命周期")]),t._v("("),n("em",[t._v("lifetimes")]),t._v("),这是一个第十章会讨论的 Rust 功能。生命周期确保结构体引用的数据有效性跟结构体本身保持一致。如果你尝试在结构体中存储一个引用而不指定生命周期将是无效的,比如这样:")]),t._v(" "),n("p",[n("span",{staticClass:"filename"},[t._v("文件名: src/main.rs")])]),t._v(" "),n("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v('struct User {\n username: &str,\n email: &str,\n sign_in_count: u64,\n active: bool,\n}\n\nfn main() {\n let user1 = User {\n email: "someone@example.com",\n username: "someusername123",\n active: true,\n sign_in_count: 1,\n };\n}\n')])])]),n("p",[t._v("编译器会抱怨它需要生命周期标识符:")]),t._v(" "),n("div",{staticClass:"language-text extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[t._v("error[E0106]: missing lifetime specifier\n --\x3e\n |\n2 | username: &str,\n | ^ expected lifetime parameter\n\nerror[E0106]: missing lifetime specifier\n --\x3e\n |\n3 | email: &str,\n | ^ expected lifetime parameter\n")])])]),n("p",[t._v("第十章会讲到如何修复这个问题以便在结构体中存储引用,不过现在,我们会使用像 "),n("code",[t._v("String")]),t._v(" 这类拥有所有权的类型来替代 "),n("code",[t._v("&str")]),t._v(" 这样的引用以修正这个错误。")])])}],!1,null,null,null);e.options.__file="ch05-01-defining-structs.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/31.247a3dbb.js b/src/.vuepress/dist/assets/js/31.247a3dbb.js
new file mode 100644
index 0000000..2d1a7fb
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/31.247a3dbb.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[31],{249:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch05-02-example-structs.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch05-02-example-structs.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),a("p",[t._v("为了理解何时会需要使用结构体,让我们编写一个计算长方形面积的程序。我们会从单独的变量开始,接着重构程序直到使用结构体替代他们为止。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),a("p",[t._v("示例 5-9 展示了使用元组的另一个程序版本。")]),t._v(" "),t._m(12),t._v(" "),t._m(13),t._m(14),t._v(" "),a("p",[t._v("在某种程度上说,这个程序更好一点了。元组帮助我们增加了一些结构性,并且现在只需传一个参数。不过在另一方面,这个版本却有一点不明确了:元组并没有给出元素的名称,所以计算变得更费解了,因为不得不使用索引来获取元组的每一部分:")]),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),a("p",[t._v("我们使用结构体为数据命名来为其赋予意义。我们可以将我们正在使用的元组转换成一个有整体名称而且每个部分也有对应名字的数据类型,如示例 5-10 所示:")]),t._v(" "),t._m(17),t._v(" "),t._m(18),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._m(27),t._v(" "),a("p",[t._v("当我们运行这个代码时,会出现带有如下核心信息的错误:")]),t._v(" "),t._m(28),t._m(29),t._v(" "),a("p",[t._v("但是如果我们继续阅读错误,将会发现这个有帮助的信息:")]),t._v(" "),t._m(30),t._m(31),t._v(" "),a("p",[t._v("这样调整后再次运行程序。见鬼了!仍然能看到一个错误:")]),t._v(" "),t._m(32),a("p",[t._v("不过编译器又一次给出了一个有帮助的信息:")]),t._v(" "),t._m(33),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._m(37),t._v(" "),a("p",[t._v("现在我们再运行这个程序时,就不会有任何错误,并会出现如下输出:")]),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"一个使用结构体的示例程序"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#一个使用结构体的示例程序","aria-hidden":"true"}},[this._v("#")]),this._v(" 一个使用结构体的示例程序")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用 Cargo 新建一个叫做 "),s("em",[this._v("rectangles")]),this._v(" 的二进制程序,它获取以像素为单位的长方形的宽度和高度,并计算出长方形的面积。示例 5-8 显示了位于项目的 "),s("em",[this._v("src/main.rs")]),this._v(" 中的小程序,它刚刚好实现此功能:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" width1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("30")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" height1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"The area of the rectangle is {} square pixels."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("area")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("width1"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" height1"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("area")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n width "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" height\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-8:通过分别指定长方形的宽和高的变量来计算长方形面积")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在使用 "),s("code",[this._v("cargo run")]),this._v(" 运行程序:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("The area of the rectangle is 1500 square pixels.\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("虽然示例 5-8 可以运行,并在调用 "),s("code",[this._v("area")]),this._v(" 函数时传入每个维度来计算出长方形的面积,不过我们可以做的更好。宽度和高度是相关联的,因为他们在一起才能定义一个长方形。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这些代码的问题突显在 "),s("code",[this._v("area")]),this._v(" 的签名上:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn area(width: u32, height: u32) -> u32 {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("函数 "),s("code",[this._v("area")]),this._v(" 本应该计算一个长方形的面积,不过函数却有两个参数。这两个参数是相关联的,不过程序本身却没有表现出这一点。将长度和宽度组合在一起将更易懂也更易处理。第三章的 “元组类型” 部分已经讨论过了一种可行的方法:元组。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用元组重构"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用元组重构","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用元组重构")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" rect1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("30")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"The area of the rectangle is {} square pixels."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("area")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("rect1"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("area")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dimensions"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n dimensions"),a("span",{attrs:{class:"token number"}},[t._v(".0")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" dimensions"),a("span",{attrs:{class:"token number"}},[t._v(".1")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-9:使用元组来指定长方形的宽高")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("在计算面积时将宽和高弄混倒无关紧要,不过当在屏幕上绘制长方形时就有问题了!我们必须牢记 "),a("code",[t._v("width")]),t._v(" 的元组索引是 "),a("code",[t._v("0")]),t._v(","),a("code",[t._v("height")]),t._v(" 的元组索引是 "),a("code",[t._v("1")]),t._v("。如果其他人要使用这些代码,他们必须要搞清楚这一点,并也要牢记于心。很容易忘记或者混淆这些值而造成错误,因为我们没有在代码中传达数据的意图。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用结构体重构:赋予更多意义"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用结构体重构:赋予更多意义","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用结构体重构:赋予更多意义")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" rect1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("30")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"The area of the rectangle is {} square pixels."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("area")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("rect1"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("area")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("rectangle"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("Rectangle"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n rectangle"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" rectangle"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("height\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-10:定义 "),s("code",[this._v("Rectangle")]),this._v(" 结构体")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这里我们定义了一个结构体并称其为 "),a("code",[t._v("Rectangle")]),t._v("。在大括号中定义了字段 "),a("code",[t._v("width")]),t._v(" 和 "),a("code",[t._v("height")]),t._v(",类型都是 "),a("code",[t._v("u32")]),t._v("。接着在 "),a("code",[t._v("main")]),t._v(" 中,我们创建了一个具体的 "),a("code",[t._v("Rectangle")]),t._v(" 实例,它的宽是 30,高是 50。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("函数 "),a("code",[t._v("area")]),t._v(" 现在被定义为接收一个名叫 "),a("code",[t._v("rectangle")]),t._v(" 的参数,其类型是一个结构体 "),a("code",[t._v("Rectangle")]),t._v(" 实例的不可变借用。第四章讲到过,我们希望借用结构体而不是获取它的所有权,这样 "),a("code",[t._v("main")]),t._v(" 函数就可以保持 "),a("code",[t._v("rect1")]),t._v(" 的所有权并继续使用它,所以这就是为什么在函数签名和调用的地方会有 "),a("code",[t._v("&")]),t._v("。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("area")]),t._v(" 函数访问 "),a("code",[t._v("Rectangle")]),t._v(" 实例的 "),a("code",[t._v("width")]),t._v(" 和 "),a("code",[t._v("height")]),t._v(" 字段。"),a("code",[t._v("area")]),t._v(" 的函数签名现在明确的阐述了我们的意图:使用 "),a("code",[t._v("Rectangle")]),t._v(" 的 "),a("code",[t._v("width")]),t._v(" 和 "),a("code",[t._v("height")]),t._v(" 字段,计算 "),a("code",[t._v("Rectangle")]),t._v(" 的面积。这表明宽高是相互联系的,并为这些值提供了描述性的名称而不是使用元组的索引值 "),a("code",[t._v("0")]),t._v(" 和 "),a("code",[t._v("1")]),t._v(" 。结构体胜在更清晰明了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"通过派生-trait-增加实用功能"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#通过派生-trait-增加实用功能","aria-hidden":"true"}},[this._v("#")]),this._v(" 通过派生 trait 增加实用功能")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果能够在调试程序时打印出 "),s("code",[this._v("Rectangle")]),this._v(" 实例来查看其所有字段的值就更好了。示例 5-11 像前面章节那样尝试使用 "),s("code",[this._v("println!")]),this._v(" 宏。但这并不行。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('struct Rectangle {\n width: u32,\n height: u32,\n}\n\nfn main() {\n let rect1 = Rectangle { width: 30, height: 50 };\n\n println!("rect1 is {}", rect1);\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-11:尝试打印出 "),s("code",[this._v("Rectangle")]),this._v(" 实例")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0277]: the trait bound `Rectangle: std::fmt::Display` is not satisfied\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("println!")]),t._v(" 宏能处理很多类型的格式,不过,"),a("code",[t._v("{}")]),t._v(" 默认告诉 "),a("code",[t._v("println!")]),t._v(" 使用被称为 "),a("code",[t._v("Display")]),t._v(" 的格式:意在提供给直接终端用户查看的输出。目前为止见过的基本类型都默认实现了 "),a("code",[t._v("Display")]),t._v(",因为它就是向用户展示 "),a("code",[t._v("1")]),t._v(" 或其他任何基本类型的唯一方式。不过对于结构体,"),a("code",[t._v("println!")]),t._v(" 应该用来输出的格式是不明确的,因为这有更多显示的可能性:是否需要逗号?需要打印出大括号吗?所有字段都应该显示吗?由于这种不确定性,Rust 不会尝试猜测我们的意图,所以结构体并没有提供一个 "),a("code",[t._v("Display")]),t._v(" 实现。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("`Rectangle` cannot be formatted with the default formatter; try using\n`:?` instead if you are using a format string\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("让我们来试试!现在 "),a("code",[t._v("println!")]),t._v(" 宏调用看起来像 "),a("code",[t._v('println!("rect1 is {:?}", rect1);')]),t._v(" 这样。在 "),a("code",[t._v("{}")]),t._v(" 中加入 "),a("code",[t._v(":?")]),t._v(" 指示符告诉 "),a("code",[t._v("println!")]),t._v(" 我们想要使用叫做 "),a("code",[t._v("Debug")]),t._v(" 的输出格式。"),a("code",[t._v("Debug")]),t._v(" 是一个 trait,它允许我们以一种对开发者有帮助的方式打印结构体,以便当我们调试代码时能看到它的值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0277]: the trait bound `Rectangle: std::fmt::Debug` is not satisfied\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("`Rectangle` cannot be formatted using `:?`; if it is defined in your\ncrate, add `#[derive(Debug)]` or manually implement it\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust "),s("strong",[this._v("确实")]),this._v(" 包含了打印出调试信息的功能,不过我们必须为结构体显式选择这个功能。为此,在结构体定义之前加上 "),s("code",[this._v("#[derive(Debug)]")]),this._v(" 注解,如示例 5-12 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" rect1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("30")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"rect1 is {:?}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" rect1"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-12:增加注解来派生 "),s("code",[this._v("Debug")]),this._v(" trait,并使用调试格式打印 "),s("code",[this._v("Rectangle")]),this._v(" 实例")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("rect1 is Rectangle { width: 30, height: 50 }\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("好极了!这并不是最漂亮的输出,不过它显示这个实例的所有字段,毫无疑问这对调试有帮助。当我们有一个更大的结构体时,能有更易读一点的输出就好了,为此可以使用 "),a("code",[t._v("{:#?}")]),t._v(" 替换 "),a("code",[t._v("println!")]),t._v(" 字符串中的 "),a("code",[t._v("{:?}")]),t._v("。如果在这个例子中使用了 "),a("code",[t._v("{:#?}")]),t._v(" 风格的话,输出会看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("rect1 is Rectangle {\n width: 30,\n height: 50\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 为我们提供了很多可以通过 "),s("code",[this._v("derive")]),this._v(" 注解来使用的 trait,他们可以为我们的自定义类型增加实用的行为。附录 C 中列出了这些 trait 和行为。第十章会介绍如何通过自定义行为来实现这些 trait,同时还有如何创建你自己的 trait。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("我们的 "),a("code",[t._v("area")]),t._v(" 函数是非常特殊的,它只计算长方形的面积。如果这个行为与 "),a("code",[t._v("Rectangle")]),t._v(" 结构体再结合得更紧密一些就更好了,因为它不能用于其他类型。现在让我们看看如何继续重构这些代码,来将 "),a("code",[t._v("area")]),t._v(" 函数协调进 "),a("code",[t._v("Rectangle")]),t._v(" 类型定义的 "),a("code",[t._v("area")]),t._v(" "),a("strong",[t._v("方法")]),t._v(" 中。")])}],!1,null,null,null);e.options.__file="ch05-02-example-structs.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/32.b1bb1e43.js b/src/.vuepress/dist/assets/js/32.b1bb1e43.js
new file mode 100644
index 0000000..552c38b
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/32.b1bb1e43.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[32],{247:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch05-03-method-syntax.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch05-03-method-syntax.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),a("p",[t._v("结构体让你可以创建出在你的领域中有意义的自定义类型。通过结构体,我们可以将相关联的数据片段联系起来并命名它们,这样可以使得代码更加清晰。方法允许为结构体实例指定行为,而关联函数将特定功能置于结构体的命名空间中并且无需一个实例。")]),t._v(" "),a("p",[t._v("但结构体并不是创建自定义类型的唯一方法:让我们转向 Rust 的枚举功能,为你的工具箱再添一个工具。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"方法语法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#方法语法","aria-hidden":"true"}},[this._v("#")]),this._v(" 方法语法")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("方法")]),this._v(" 与函数类似:它们使用 "),s("code",[this._v("fn")]),this._v(" 关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码。不过方法与函数是不同的,因为它们在结构体的上下文中被定义(或者是枚举或 trait 对象的上下文,将分别在第六章和第十七章讲解),并且它们第一个参数总是 "),s("code",[this._v("self")]),this._v(",它代表调用该方法的结构体实例。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"定义方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#定义方法","aria-hidden":"true"}},[this._v("#")]),this._v(" 定义方法")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("让我们把前面实现的获取一个 "),a("code",[t._v("Rectangle")]),t._v(" 实例作为参数的 "),a("code",[t._v("area")]),t._v(" 函数,改写成一个定义于 "),a("code",[t._v("Rectangle")]),t._v(" 结构体上的 "),a("code",[t._v("area")]),t._v(" 方法,如示例 5-13 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("area")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("height\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" rect1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("30")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{attrs:{class:"token string"}},[t._v('"The area of the rectangle is {} square pixels."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n rect1"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("area")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-13:在 "),s("code",[this._v("Rectangle")]),this._v(" 结构体上定义 "),s("code",[this._v("area")]),this._v(" 方法")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("为了使函数定义于 "),a("code",[t._v("Rectangle")]),t._v(" 的上下文中,我们开始了一个 "),a("code",[t._v("impl")]),t._v(" 块("),a("code",[t._v("impl")]),t._v(" 是 "),a("em",[t._v("implementation")]),t._v(" 的缩写)。接着将 "),a("code",[t._v("area")]),t._v(" 函数移动到 "),a("code",[t._v("impl")]),t._v(" 大括号中,并将签名中的第一个(在这里也是唯一一个)参数和函数体中其他地方的对应参数改成 "),a("code",[t._v("self")]),t._v("。然后在 "),a("code",[t._v("main")]),t._v(" 中将我们先前调用 "),a("code",[t._v("area")]),t._v(" 方法并传递 "),a("code",[t._v("rect1")]),t._v(" 作为参数的地方,改成使用 "),a("strong",[t._v("方法语法")]),t._v("("),a("em",[t._v("method syntax")]),t._v(")在 "),a("code",[t._v("Rectangle")]),t._v(" 实例上调用 "),a("code",[t._v("area")]),t._v(" 方法。方法语法获取一个实例并加上一个点号,后跟方法名、圆括号以及任何参数。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("在 "),a("code",[t._v("area")]),t._v(" 的签名中,使用 "),a("code",[t._v("&self")]),t._v(" 来替代 "),a("code",[t._v("rectangle: &Rectangle")]),t._v(",因为该方法位于 "),a("code",[t._v("impl Rectangle")]),t._v(" 上下文中所以 Rust 知道 "),a("code",[t._v("self")]),t._v(" 的类型是 "),a("code",[t._v("Rectangle")]),t._v("。注意仍然需要在 "),a("code",[t._v("self")]),t._v(" 前面加上 "),a("code",[t._v("&")]),t._v(",就像 "),a("code",[t._v("&Rectangle")]),t._v(" 一样。方法可以选择获取 "),a("code",[t._v("self")]),t._v(" 的所有权,或者像我们这里一样不可变地借用 "),a("code",[t._v("self")]),t._v(",或者可变地借用 "),a("code",[t._v("self")]),t._v(",就跟其他参数一样。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这里选择 "),a("code",[t._v("&self")]),t._v(" 的理由跟在函数版本中使用 "),a("code",[t._v("&Rectangle")]),t._v(" 是相同的:我们并不想获取所有权,只希望能够读取结构体中的数据,而不是写入。如果想要在方法中改变调用方法的实例,需要将第一个参数改为 "),a("code",[t._v("&mut self")]),t._v("。通过仅仅使用 "),a("code",[t._v("self")]),t._v(" 作为第一个参数来使方法获取实例的所有权是很少见的;这种技术通常用在当方法将 "),a("code",[t._v("self")]),t._v(" 转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用方法替代函数,除了可使用方法语法和不需要在每个函数签名中重复 "),s("code",[this._v("self")]),this._v(" 的类型之外,其主要好处在于组织性。我们将某个类型实例能做的所有事情都一起放入 "),s("code",[this._v("impl")]),this._v(" 块中,而不是让将来的用户在我们的库中到处寻找 "),s("code",[this._v("Rectangle")]),this._v(" 的功能。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("blockquote",[a("h3",{attrs:{id:"运算符到哪去了?"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#运算符到哪去了?","aria-hidden":"true"}},[t._v("#")]),t._v(" "),a("code",[t._v("->")]),t._v(" 运算符到哪去了?")]),t._v(" "),a("p",[t._v("在 C/C++ 语言中,有两个不同的运算符来调用方法:"),a("code",[t._v(".")]),t._v(" 直接在对象上调用方法,而 "),a("code",[t._v("->")]),t._v(" 在一个对象的指针上调用方法,这时需要先解引用(dereference)指针。换句话说,如果 "),a("code",[t._v("object")]),t._v(" 是一个指针,那么 "),a("code",[t._v("object->something()")]),t._v(" 就像 "),a("code",[t._v("(*object).something()")]),t._v(" 一样。")]),t._v(" "),a("p",[t._v("Rust 并没有一个与 "),a("code",[t._v("->")]),t._v(" 等效的运算符;相反,Rust 有一个叫 "),a("strong",[t._v("自动引用和解引用")]),t._v("("),a("em",[t._v("automatic referencing and dereferencing")]),t._v(")的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。")]),t._v(" "),a("p",[t._v("他是这样工作的:当使用 "),a("code",[t._v("object.something()")]),t._v(" 调用方法时,Rust 会自动为 "),a("code",[t._v("object")]),t._v(" 添加 "),a("code",[t._v("&")]),t._v("、"),a("code",[t._v("&mut")]),t._v(" 或 "),a("code",[t._v("*")]),t._v(" 以便使 "),a("code",[t._v("object")]),t._v(" 与方法签名匹配。也就是说,这些代码是等价的:")]),t._v(" "),a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug,Copy,Clone)]")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" f64"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" f64"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("distance")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("Point"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" f64 "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x_squared "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" f64"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("powi")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("other"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x "),a("span",{attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" y_squared "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" f64"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("powi")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("other"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("y "),a("span",{attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("y"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# f64"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("sqrt")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x_squared "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" y_squared"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" p1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("0.0")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("0.0")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" p2 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5.0")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("6.5")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\np1"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("distance")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("p2"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("p1"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("distance")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("p2"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("第一行看起来简洁的多。这种自动引用的行为之所以有效,是因为方法有一个明确的接收者———— "),a("code",[t._v("self")]),t._v(" 的类型。在给出接收者和方法名的前提下,Rust 可以明确地计算出方法是仅仅读取("),a("code",[t._v("&self")]),t._v("),做出修改("),a("code",[t._v("&mut self")]),t._v(")或者是获取所有权("),a("code",[t._v("self")]),t._v(")。事实上,Rust 对方法接收者的隐式借用让所有权在实践中更友好。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"带有更多参数的方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#带有更多参数的方法","aria-hidden":"true"}},[this._v("#")]),this._v(" 带有更多参数的方法")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("让我们通过实现 "),a("code",[t._v("Rectangle")]),t._v(" 结构体上的另一方法来练习使用方法。这回,我们让一个 "),a("code",[t._v("Rectangle")]),t._v(" 的实例获取另一个 "),a("code",[t._v("Rectangle")]),t._v(" 实例,如果 "),a("code",[t._v("self")]),t._v(" 能完全包含第二个长方形则返回 "),a("code",[t._v("true")]),t._v(";否则返回 "),a("code",[t._v("false")]),t._v("。一旦定义了 "),a("code",[t._v("can_hold")]),t._v(" 方法,就可以编写示例 5-14 中的代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let rect1 = Rectangle { width: 30, height: 50 };\n let rect2 = Rectangle { width: 10, height: 40 };\n let rect3 = Rectangle { width: 60, height: 45 };\n\n println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));\n println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-14:使用还未实现的 "),s("code",[this._v("can_hold")]),this._v(" 方法")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("同时我们希望看到如下输出,因为 "),a("code",[t._v("rect2")]),t._v(" 的两个维度都小于 "),a("code",[t._v("rect1")]),t._v(",而 "),a("code",[t._v("rect3")]),t._v(" 比 "),a("code",[t._v("rect1")]),t._v(" 要宽:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("Can rect1 hold rect2? true\nCan rect1 hold rect3? false\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("因为我们想定义一个方法,所以它应该位于 "),a("code",[t._v("impl Rectangle")]),t._v(" 块中。方法名是 "),a("code",[t._v("can_hold")]),t._v(",并且它会获取另一个 "),a("code",[t._v("Rectangle")]),t._v(" 的不可变借用作为参数。通过观察调用方法的代码可以看出参数是什么类型的:"),a("code",[t._v("rect1.can_hold(&rect2)")]),t._v(" 传入了 "),a("code",[t._v("&rect2")]),t._v(",它是一个 "),a("code",[t._v("Rectangle")]),t._v(" 的实例 "),a("code",[t._v("rect2")]),t._v(" 的不可变借用。这是可以理解的,因为我们只需要读取 "),a("code",[t._v("rect2")]),t._v("(而不是写入,这意味着我们需要一个可变借用),而且希望 "),a("code",[t._v("main")]),t._v(" 保持 "),a("code",[t._v("rect2")]),t._v(" 的所有权,这样就可以在调用这个方法后继续使用它。"),a("code",[t._v("can_hold")]),t._v(" 的返回值是一个布尔值,其实现会分别检查 "),a("code",[t._v("self")]),t._v(" 的宽高是否都大于另一个 "),a("code",[t._v("Rectangle")]),t._v("。让我们在示例 5-13 的 "),a("code",[t._v("impl")]),t._v(" 块中增加这个新的 "),a("code",[t._v("can_hold")]),t._v(" 方法,如示例 5-15 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("area")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("height\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("can_hold")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("Rectangle"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" bool "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width "),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width "),a("span",{attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("height "),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("height\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-15:在 "),s("code",[this._v("Rectangle")]),this._v(" 上实现 "),s("code",[this._v("can_hold")]),this._v(" 方法,它获取另一个 "),s("code",[this._v("Rectangle")]),this._v(" 实例作为参数")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果结合示例 5-14 的 "),s("code",[this._v("main")]),this._v(" 函数来运行,就会看到期望的输出。在方法签名中,可以在 "),s("code",[this._v("self")]),this._v(" 后增加多个参数,而且这些参数就像函数中的参数一样工作。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"关联函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#关联函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 关联函数")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("impl")]),t._v(" 块的另一个有用的功能是:允许在 "),a("code",[t._v("impl")]),t._v(" 块中定义 "),a("strong",[t._v("不")]),t._v(" 以 "),a("code",[t._v("self")]),t._v(" 作为参数的函数。这被称为 "),a("strong",[t._v("关联函数")]),t._v("("),a("em",[t._v("associated functions")]),t._v("),因为它们与结构体相关联。它们仍是函数而不是方法,因为它们并不作用于一个结构体的实例。你已经使用过 "),a("code",[t._v("String::from")]),t._v(" 关联函数了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("关联函数经常被用作返回一个结构体新实例的构造函数。例如我们可以提供一个关联函数,它接受一个维度参数并且同时作为宽和高,这样可以更轻松的创建一个正方形 "),s("code",[this._v("Rectangle")]),this._v(" 而不必指定两次同样的值:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("square")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" size"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" size "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用结构体名和 "),s("code",[this._v("::")]),this._v(" 语法来调用这个关联函数:比如 "),s("code",[this._v("let sq = Rectangle::square(3);")]),this._v("。这个方法位于结构体的命名空间中:"),s("code",[this._v("::")]),this._v(" 语法用于关联函数和模块创建的命名空间。第七章会讲到模块。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"多个-impl-块"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#多个-impl-块","aria-hidden":"true"}},[this._v("#")]),this._v(" 多个 "),s("code",[this._v("impl")]),this._v(" 块")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("每个结构体都允许拥有多个 "),s("code",[this._v("impl")]),this._v(" 块。例如,示例 5-16 中的代码等同于示例 5-15,但每个方法有其自己的 "),s("code",[this._v("impl")]),this._v(" 块。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# height"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("area")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("height\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("can_hold")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("Rectangle"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" bool "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width "),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width "),a("span",{attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("height "),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("height\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 5-16:使用多个 "),s("code",[this._v("impl")]),this._v(" 块重写示例 5-15")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里没有理由将这些方法分散在多个 "),s("code",[this._v("impl")]),this._v(" 块中,不过这是有效的语法。第十章讨论泛型和 trait 时会看到实用的多 "),s("code",[this._v("impl")]),this._v(" 块的用例。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])}],!1,null,null,null);e.options.__file="ch05-03-method-syntax.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/33.3656c740.js b/src/.vuepress/dist/assets/js/33.3656c740.js
new file mode 100644
index 0000000..bf4526d
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/33.3656c740.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[33],{241:function(t,e,n){"use strict";n.r(e);var s=n(0),r=Object(s.a)({},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch06-00-enums.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch06-00-enums.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"枚举和模式匹配"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#枚举和模式匹配","aria-hidden":"true"}},[this._v("#")]),this._v(" 枚举和模式匹配")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[t._v("本章介绍 "),n("strong",[t._v("枚举")]),t._v("("),n("em",[t._v("enumerations")]),t._v("),也被称作 "),n("em",[t._v("enums")]),t._v("。枚举允许你通过列举可能的值来定义一个类型。首先,我们会定义并使用一个枚举来展示它是如何连同数据一起编码信息的。接下来,我们会探索一个特别有用的枚举,叫做 "),n("code",[t._v("Option")]),t._v(",它代表一个值要么是某个值要么什么都不是。然后会讲到在 "),n("code",[t._v("match")]),t._v(" 表达式中用模式匹配,针对不同的枚举值编写相应要执行的代码。最后会介绍 "),n("code",[t._v("if let")]),t._v(",另一个简洁方便处理代码中枚举的结构。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("枚举是一个很多语言都有的功能,不过不同语言中其功能各不相同。Rust 的枚举与 F#、OCaml 和 Haskell 这样的函数式编程语言中的 "),e("strong",[this._v("代数数据类型")]),this._v("("),e("em",[this._v("algebraic data types")]),this._v(")最为相似。")])}],!1,null,null,null);r.options.__file="ch06-00-enums.md";e.default=r.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/34.03c1b47c.js b/src/.vuepress/dist/assets/js/34.03c1b47c.js
new file mode 100644
index 0000000..e8140f4
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/34.03c1b47c.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[34],{236:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch06-01-defining-an-enum.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch06-01-defining-an-enum.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),n("p",[t._v("任何一个 IP 地址要么是 IPv4 的要么是 IPv6 的,而且不能两者都是。IP 地址的这个特性使得枚举数据结构非常适合这个场景,因为枚举值只可能是其中一个成员。IPv4 和 IPv6 从根本上讲仍是 IP 地址,所以当代码在处理适用于任何类型的 IP 地址的场景时应该把它们当作相同的类型。")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._m(8),t._v(" "),t._m(9),n("p",[t._v("现在可以使用任一成员来调用这个函数:")]),t._v(" "),t._m(10),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),n("p",[t._v("我们直接将数据附加到枚举的每个成员上,这样就不需要一个额外的结构体了。")]),t._v(" "),t._m(17),t._v(" "),t._m(18),n("p",[t._v("这些代码展示了使用枚举来存储两种不同 IP 地址的几种可能的选择。然而,事实证明存储和编码 IP 地址实在是太常见了"),n("a",{attrs:{href:"https://doc.rust-lang.org/std/net/enum.IpAddr.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("以致标准库提供了一个开箱即用的定义!"),n("OutboundLink")],1),t._v("让我们看看标准库是如何定义 "),n("code",[t._v("IpAddr")]),t._v(" 的:它正有着跟我们定义和使用的一样的枚举和成员,不过它将成员中的地址数据嵌入到了两个不同形式的结构体中,它们对不同的成员的定义是不同的:")]),t._v(" "),t._m(19),n("p",[t._v("这些代码展示了可以将任意类型的数据放入枚举成员中:例如字符串、数字类型或者结构体。甚至可以包含另一个枚举!另外,标准库中的类型通常并不比你设想出来的要复杂多少。")]),t._v(" "),t._m(20),t._v(" "),n("p",[t._v("来看看示例 6-2 中的另一个枚举的例子:它的成员中内嵌了多种多样的类型:")]),t._v(" "),t._m(21),t._m(22),t._v(" "),n("p",[t._v("这个枚举有四个含有不同类型的成员:")]),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),n("p",[t._v("Tony Hoare,null 的发明者,在他 2009 年的演讲 “Null References: The Billion Dollar Mistake” 中曾经说到:")]),t._v(" "),t._m(34),t._v(" "),n("p",[t._v("空值的问题在于当你尝试像一个非空值那样使用一个空值,会出现某种形式的错误。因为空和非空的属性无处不在,非常容易出现这类错误。")]),t._v(" "),n("p",[t._v("然而,空值尝试表达的概念仍然是有意义的:空值是一个因为某种原因目前无效或缺失的值。")]),t._v(" "),n("p",[t._v("问题不在于概念而在于具体的实现。为此,Rust 并没有空值,不过它确实拥有一个可以编码存在或不存在概念的枚举。这个枚举是 "),n("code",[t._v("Option")]),t._v(",而且它"),n("a",{attrs:{href:"https://doc.rust-lang.org/std/option/enum.Option.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("定义于标准库中"),n("OutboundLink")],1),t._v(",如下:")]),t._v(" "),t._m(35),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),n("p",[t._v("如果运行这些代码,将得到类似这样的错误信息:")]),t._v(" "),t._m(43),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._v(" "),n("p",[t._v("那么当有一个 "),n("code",[t._v("Option")]),t._v(" 的值时,如何从 "),n("code",[t._v("Some")]),t._v(" 成员中取出 "),n("code",[t._v("T")]),t._v(" 的值来使用它呢?"),n("code",[t._v("Option")]),t._v(" 枚举拥有大量用于各种情况的方法:你可以查看"),n("a",{attrs:{href:"https://doc.rust-lang.org/std/option/enum.Option.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("它的文档"),n("OutboundLink")],1),t._v("。熟悉 "),n("code",[t._v("Option")]),t._v(" 的方法将对你的 Rust 之旅非常有用。")]),t._v(" "),t._m(47)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"定义枚举"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#定义枚举","aria-hidden":"true"}},[this._v("#")]),this._v(" 定义枚举")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们看看一个需要诉诸于代码的场景,来考虑为何此时使用枚举更为合适且实用。假设我们要处理 IP 地址。目前被广泛使用的两个主要 IP 标准:IPv4(version four)和 IPv6(version six)。这是我们的程序可能会遇到的所有可能的 IP 地址类型:所以可以 "),s("strong",[this._v("枚举")]),this._v(" 出所有可能的值,这也正是此枚举名字的由来。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("可以通过在代码中定义一个 "),n("code",[t._v("IpAddrKind")]),t._v(" 枚举来表现这个概念并列出可能的 IP 地址类型,"),n("code",[t._v("V4")]),t._v(" 和 "),n("code",[t._v("V6")]),t._v("。这被称为枚举的 "),n("strong",[t._v("成员")]),t._v("("),n("em",[t._v("variants")]),t._v("):")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" IpAddrKind "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n V4"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n V6"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在 "),s("code",[this._v("IpAddrKind")]),this._v(" 就是一个可以在代码中使用的自定义数据类型了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"枚举值"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#枚举值","aria-hidden":"true"}},[this._v("#")]),this._v(" 枚举值")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以像这样创建 "),s("code",[this._v("IpAddrKind")]),this._v(" 两个不同成员的实例:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" IpAddrKind "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# V4"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# V6"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" four "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" IpAddrKind"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("V4"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" six "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" IpAddrKind"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("V6"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意枚举的成员位于其标识符的命名空间中,并使用两个冒号分开。这么设计的益处是现在 "),n("code",[t._v("IpAddrKind::V4")]),t._v(" 和 "),n("code",[t._v("IpAddrKind::V6")]),t._v(" 都是 "),n("code",[t._v("IpAddrKind")]),t._v(" 类型的。例如,接着可以定义一个函数来获取任何 "),n("code",[t._v("IpAddrKind")]),t._v(":")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" IpAddrKind "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# V4"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# V6"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("route")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ip_type"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" IpAddrKind"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" IpAddrKind "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# V4"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# V6"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("route")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ip_type"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" IpAddrKind"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token function"}},[t._v("route")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("IpAddrKind"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("V4"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token function"}},[t._v("route")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("IpAddrKind"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("V6"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用枚举甚至还有更多优势。进一步考虑一下我们的 IP 地址类型,目前没有一个存储实际 IP 地址 "),s("strong",[this._v("数据")]),this._v(" 的方法;只知道它是什么 "),s("strong",[this._v("类型")]),this._v(" 的。考虑到已经在第五章学习过结构体了,你可能会像示例 6-1 那样处理这个问题:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" IpAddrKind "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n V4"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n V6"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" IpAddr "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n kind"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" IpAddrKind"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n address"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" home "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" IpAddr "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n kind"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" IpAddrKind"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("V4"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n address"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"127.0.0.1"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" loopback "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" IpAddr "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n kind"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" IpAddrKind"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("V6"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n address"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"::1"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 6-1:将 IP 地址的数据和 "),s("code",[this._v("IpAddrKind")]),this._v(" 成员存储在一个 "),s("code",[this._v("struct")]),this._v(" 中")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里我们定义了一个有两个字段的结构体 "),n("code",[t._v("IpAddr")]),t._v(":"),n("code",[t._v("IpAddrKind")]),t._v("(之前定义的枚举)类型的 "),n("code",[t._v("kind")]),t._v(" 字段和 "),n("code",[t._v("String")]),t._v(" 类型 "),n("code",[t._v("address")]),t._v(" 字段。我们有这个结构体的两个实例。第一个,"),n("code",[t._v("home")]),t._v(",它的 "),n("code",[t._v("kind")]),t._v(" 的值是 "),n("code",[t._v("IpAddrKind::V4")]),t._v(" 与之相关联的地址数据是 "),n("code",[t._v("127.0.0.1")]),t._v("。第二个实例,"),n("code",[t._v("loopback")]),t._v(","),n("code",[t._v("kind")]),t._v(" 的值是 "),n("code",[t._v("IpAddrKind")]),t._v(" 的另一个成员,"),n("code",[t._v("V6")]),t._v(",关联的地址是 "),n("code",[t._v("::1")]),t._v("。我们使用了一个结构体来将 "),n("code",[t._v("kind")]),t._v(" 和 "),n("code",[t._v("address")]),t._v(" 打包在一起,现在枚举成员就与值相关联了。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们可以使用一种更简洁的方式来表达相同的概念,仅仅使用枚举并将数据直接放进每一个枚举成员而不是将枚举作为结构体的一部分。"),n("code",[t._v("IpAddr")]),t._v(" 枚举的新定义表明了 "),n("code",[t._v("V4")]),t._v(" 和 "),n("code",[t._v("V6")]),t._v(" 成员都关联了 "),n("code",[t._v("String")]),t._v(" 值:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" IpAddr "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("V4")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("V6")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" home "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" IpAddr"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("V4")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"127.0.0.1"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" loopback "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" IpAddr"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("V6")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"::1"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("用枚举替代结构体还有另一个优势:每个成员可以处理不同类型和数量的数据。IPv4 版本的 IP 地址总是含有四个值在 0 和 255 之间的数字部分。如果我们想要将 "),n("code",[t._v("V4")]),t._v(" 地址存储为四个 "),n("code",[t._v("u8")]),t._v(" 值而 "),n("code",[t._v("V6")]),t._v(" 地址仍然表现为一个 "),n("code",[t._v("String")]),t._v(",这就不能使用结构体了。枚举则可以轻易处理的这个情况:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" IpAddr "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("V4")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("u8"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" u8"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" u8"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" u8"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("V6")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" home "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" IpAddr"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("V4")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("127")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" loopback "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" IpAddr"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("V6")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"::1"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Ipv4Addr "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Ipv6Addr "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" IpAddr "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("V4")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Ipv4Addr"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("V6")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Ipv6Addr"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("注意虽然标准库中包含一个 "),s("code",[this._v("IpAddr")]),this._v(" 的定义,仍然可以创建和使用我们自己的定义而不会有冲突,因为我们并没有将标准库中的定义引入作用域。第七章会讲到如何导入类型。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Message "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Quit"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Move "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Write")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("ChangeColor")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 6-2:一个 "),s("code",[this._v("Message")]),this._v(" 枚举,其每个成员都存储了不同数量和类型的值")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ul",[n("li",[n("code",[t._v("Quit")]),t._v(" 没有关联任何数据。")]),t._v(" "),n("li",[n("code",[t._v("Move")]),t._v(" 包含一个匿名结构体。")]),t._v(" "),n("li",[n("code",[t._v("Write")]),t._v(" 包含单独一个 "),n("code",[t._v("String")]),t._v("。")]),t._v(" "),n("li",[n("code",[t._v("ChangeColor")]),t._v(" 包含三个 "),n("code",[t._v("i32")]),t._v("。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("定义一个如示例 6-2 中所示那样的有关联值的枚举的方式和定义多个不同类型的结构体的方式很相像,除了枚举不使用 "),s("code",[this._v("struct")]),this._v(" 关键字以及其所有成员都被组合在一起位于 "),s("code",[this._v("Message")]),this._v(" 类型下。如下这些结构体可以包含与之前枚举成员中相同的数据:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" QuitMessage"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 类单元结构体")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" MoveMessage "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("WriteMessage")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 元组结构体")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("ChangeColorMessage")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 元组结构体")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不过,如果我们使用不同的结构体,由于它们都有不同的类型,我们将不能像使用示例 6-2 中定义的 "),s("code",[this._v("Message")]),this._v(" 枚举那样,轻易的定义一个能够处理这些不同类型的结构体的函数,因为枚举是单独一个类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("结构体和枚举还有另一个相似点:就像可以使用 "),s("code",[this._v("impl")]),this._v(" 来为结构体定义方法那样,也可以在枚举上定义方法。这是一个定义于我们 "),s("code",[this._v("Message")]),this._v(" 枚举上的叫做 "),s("code",[this._v("call")]),this._v(" 的方法:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Message "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# Quit"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Move "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("Write")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("ChangeColor")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Message "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("call")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 在这里定义方法体")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" m "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Message"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("Write")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nm"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("call")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("方法体使用了 "),n("code",[t._v("self")]),t._v(" 来获取调用方法的值。这个例子中,创建了一个值为 "),n("code",[t._v('Message::Write(String::from("hello"))')]),t._v(" 的变量 "),n("code",[t._v("m")]),t._v(",而且这就是当 "),n("code",[t._v("m.call()")]),t._v(" 运行时 "),n("code",[t._v("call")]),t._v(" 方法中的 "),n("code",[t._v("self")]),t._v(" 的值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们看看标准库中的另一个非常常见且实用的枚举:"),s("code",[this._v("Option")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"option-枚举和其相对于空值的优势"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#option-枚举和其相对于空值的优势","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("Option")]),this._v(" 枚举和其相对于空值的优势")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在之前的部分,我们看到了 "),n("code",[t._v("IpAddr")]),t._v(" 枚举如何利用 Rust 的类型系统在程序中编码更多信息而不单单是数据。接下来我们分析一个 "),n("code",[t._v("Option")]),t._v(" 的案例,"),n("code",[t._v("Option")]),t._v(" 是标准库定义的另一个枚举。"),n("code",[t._v("Option")]),t._v(" 类型应用广泛因为它编码了一个非常普遍的场景,即一个值要么有值要么没值。从类型系统的角度来表达这个概念就意味着编译器需要检查是否处理了所有应该处理的情况,这样就可以避免在其他编程语言中非常常见的 bug。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("编程语言的设计经常要考虑包含哪些功能,但考虑排除哪些功能也很重要。Rust 并没有很多其他语言中有的空值功能。"),s("strong",[this._v("空值")]),this._v("("),s("em",[this._v("Null")]),this._v(" )是一个值,它代表没有值。在有空值的语言中,变量总是这两种状态之一:空值和非空值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("blockquote",[s("p",[this._v("I call it my billion-dollar mistake. At that time, I was designing the first\ncomprehensive type system for references in an object-oriented language. My\ngoal was to ensure that all use of references should be absolutely safe, with\nchecking performed automatically by the compiler. But I couldn't resist the\ntemptation to put in a null reference, simply because it was so easy to\nimplement. This has led to innumerable errors, vulnerabilities, and system\ncrashes, which have probably caused a billion dollars of pain and damage in\nthe last forty years.")]),this._v(" "),s("p",[this._v("我称之为我十亿美元的错误。当时,我在为一个面向对象语言设计第一个综合性的面向引用的类型系统。我的目标是通过编译器的自动检查来保证所有引用的使用都应该是绝对安全的。不过我未能抵抗住引入一个空引用的诱惑,仅仅是因为它是这么的容易实现。这引发了无数错误、漏洞和系统崩溃,在之后的四十多年中造成了数十亿美元的苦痛和伤害。")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n None"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Option")]),t._v(" 枚举是如此有用以至于它甚至被包含在了 prelude 之中,你不需要将其显式引入作用域。另外,它的成员也是如此,可以不需要 "),n("code",[t._v("Option::")]),t._v(" 前缀来直接使用 "),n("code",[t._v("Some")]),t._v(" 和 "),n("code",[t._v("None")]),t._v("。即便如此 "),n("code",[t._v("Option")]),t._v(" 也仍是常规的枚举,"),n("code",[t._v("Some(T)")]),t._v(" 和 "),n("code",[t._v("None")]),t._v(" 仍是 "),n("code",[t._v("Option")]),t._v(" 的成员。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("")]),t._v(" 语法是一个我们还未讲到的 Rust 功能。它是一个泛型类型参数,第十章会更详细的讲解泛型。目前,所有你需要知道的就是 "),n("code",[t._v("")]),t._v(" 意味着 "),n("code",[t._v("Option")]),t._v(" 枚举的 "),n("code",[t._v("Some")]),t._v(" 成员可以包含任意类型的数据。这里是一些包含数字类型和字符串类型 "),n("code",[t._v("Option")]),t._v(" 值的例子:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" some_number "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" some_string "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"a string"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" absent_number"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("i32"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" None"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如果使用 "),n("code",[t._v("None")]),t._v(" 而不是 "),n("code",[t._v("Some")]),t._v(",需要告诉 Rust "),n("code",[t._v("Option")]),t._v(" 是什么类型的,因为编译器只通过 "),n("code",[t._v("None")]),t._v(" 值无法推断出 "),n("code",[t._v("Some")]),t._v(" 成员保存的值的类型。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当有一个 "),n("code",[t._v("Some")]),t._v(" 值时,我们就知道存在一个值,而这个值保存在 "),n("code",[t._v("Some")]),t._v(" 中。当有个 "),n("code",[t._v("None")]),t._v(" 值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。那么,"),n("code",[t._v("Option")]),t._v(" 为什么就比空值要好呢?")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("简而言之,因为 "),n("code",[t._v("Option")]),t._v(" 和 "),n("code",[t._v("T")]),t._v("(这里 "),n("code",[t._v("T")]),t._v(" 可以是任何类型)是不同的类型,编译器不允许像一个肯定有效的值那样使用 "),n("code",[t._v("Option")]),t._v("。例如,这段代码不能编译,因为它尝试将 "),n("code",[t._v("Option")]),t._v(" 与 "),n("code",[t._v("i8")]),t._v(" 相加:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("let x: i8 = 5;\nlet y: Option = Some(5);\n\nlet sum = x + y;\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0277]: the trait bound `i8: std::ops::Add>` is\nnot satisfied\n --\x3e\n |\n5 | let sum = x + y;\n | ^ no implementation for `i8 + std::option::Option`\n |\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("哇哦!事实上,错误信息意味着 Rust 不知道该如何将 "),n("code",[t._v("Option")]),t._v(" 与 "),n("code",[t._v("i8")]),t._v(" 相加,因为它们的类型不同。当在 Rust 中拥有一个像 "),n("code",[t._v("i8")]),t._v(" 这样类型的值时,编译器确保它总是有一个有效的值。我们可以自信使用而无需做空值检查。只有当使用 "),n("code",[t._v("Option")]),t._v("(或者任何用到的类型)的时候需要担心可能没有值,而编译器会确保我们在使用值之前处理了为空的情况。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("换句话说,在对 "),s("code",[this._v("Option")]),this._v(" 进行 "),s("code",[this._v("T")]),this._v(" 的运算之前必须将其转换为 "),s("code",[this._v("T")]),this._v("。通常这能帮助我们捕获到空值最常见的问题之一:假设某值不为空但实际上为空的情况。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不再担心会错误的假设一个非空值,会让你对代码更加有信心。为了拥有一个可能为空的值,你必须要显式的将其放入对应类型的 "),s("code",[this._v("Option")]),this._v(" 中。接着,当使用这个值时,必须明确的处理值为空的情况。只要一个值不是 "),s("code",[this._v("Option")]),this._v(" 类型,你就 "),s("strong",[this._v("可以")]),this._v(" 安全的认定它的值不为空。这是 Rust 的一个经过深思熟虑的设计决策,来限制空值的泛滥以增加 Rust 代码的安全性。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("总的来说,为了使用 "),n("code",[t._v("Option")]),t._v(" 值,需要编写处理每个成员的代码。你想要一些代码只当拥有 "),n("code",[t._v("Some(T)")]),t._v(" 值时运行,允许这些代码使用其中的 "),n("code",[t._v("T")]),t._v("。也希望一些代码在值为 "),n("code",[t._v("None")]),t._v(" 时运行,这些代码并没有一个可用的 "),n("code",[t._v("T")]),t._v(" 值。"),n("code",[t._v("match")]),t._v(" 表达式就是这么一个处理枚举的控制流结构:它会根据枚举的成员运行不同的代码,这些代码可以使用匹配到的值中的数据。")])}],!1,null,null,null);e.options.__file="ch06-01-defining-an-enum.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/35.5af5d342.js b/src/.vuepress/dist/assets/js/35.5af5d342.js
new file mode 100644
index 0000000..63a75a8
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/35.5af5d342.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[35],{235:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch06-02-match.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch06-02-match.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),n("p",[t._v("匹配分支的另一个有用的功能是可以绑定匹配的模式的部分值。这也就是如何从枚举成员中提取值的。")]),t._v(" "),t._m(13),t._v(" "),t._m(14),t._m(15),t._v(" "),n("p",[t._v("想象一下我们的一个朋友尝试收集所有 50 个州的 25 美分硬币。在根据硬币类型分类零钱的同时,也可以报告出每个 25 美分硬币所对应的州名称,这样如果我们的朋友没有的话,他可以将其加入收藏。")]),t._v(" "),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._m(28),t._v(" "),t._m(29),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._m(38),t._v(" "),t._m(39),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._m(44),t._v(" "),t._m(45)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"match-控制流运算符"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#match-控制流运算符","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("match")]),this._v(" 控制流运算符")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 有一个叫做 "),s("code",[this._v("match")]),this._v(" 的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较并根据相匹配的模式执行相应代码。模式可由字面值、变量、通配符和许多其他内容构成;第十八章会涉及到所有不同种类的模式以及它们的作用。"),s("code",[this._v("match")]),this._v(" 的力量来源于模式的表现力以及编译器检查,它确保了所有可能的情况都得到处理。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以把 "),s("code",[this._v("match")]),this._v(" 表达式想象成某种硬币分类器:硬币滑入有着不同大小孔洞的轨道,每一个硬币都会掉入符合它大小的孔洞。同样地,值也会通过 "),s("code",[this._v("match")]),this._v(" 的每一个模式,并且在遇到第一个 “符合” 的模式时,值会进入相关联的代码块并在执行中被使用。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为刚刚提到了硬币,让我们用它们来作为一个使用 "),s("code",[this._v("match")]),this._v(" 的例子!我们可以编写一个函数来获取一个未知的(美帝)硬币,并以一种类似验钞机的方式,确定它是何种硬币并返回它的美分值,如示例 6-3 中所示:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Coin "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Penny"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Nickel"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Dime"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Quarter"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("value_in_cents")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" coin "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Penny "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Nickel "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Dime "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Quarter "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("25")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 6-3:一个枚举和一个以枚举成员作为模式的 "),s("code",[this._v("match")]),this._v(" 表达式")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("拆开 "),n("code",[t._v("value_in_cents")]),t._v(" 函数中的 "),n("code",[t._v("match")]),t._v(" 来看。首先,我们列出 "),n("code",[t._v("match")]),t._v(" 关键字后跟一个表达式,在这个例子中是 "),n("code",[t._v("coin")]),t._v(" 的值。这看起来非常像 "),n("code",[t._v("if")]),t._v(" 使用的表达式,不过这里有一个非常大的区别:对于 "),n("code",[t._v("if")]),t._v(",表达式必须返回一个布尔值,而这里它可以是任何类型的。例子中的 "),n("code",[t._v("coin")]),t._v(" 的类型是示例 6-3 中定义的 "),n("code",[t._v("Coin")]),t._v(" 枚举。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("接下来是 "),n("code",[t._v("match")]),t._v(" 的分支。一个分支有两个部分:一个模式和一些代码。第一个分支的模式是值 "),n("code",[t._v("Coin::Penny")]),t._v(" 而之后的 "),n("code",[t._v("=>")]),t._v(" 运算符将模式和将要运行的代码分开。这里的代码就仅仅是值 "),n("code",[t._v("1")]),t._v("。每一个分支之间使用逗号分隔。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当 "),s("code",[this._v("match")]),this._v(" 表达式执行时,它将结果值按顺序与每一个分支的模式相比较。如果模式匹配了这个值,这个模式相关联的代码将被执行。如果模式并不匹配这个值,将继续执行下一个分支,非常类似一个硬币分类器。可以拥有任意多的分支:示例 6-3 中的 "),s("code",[this._v("match")]),this._v(" 有四个分支。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个 "),s("code",[this._v("match")]),this._v(" 表达式的返回值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果分支代码较短的话通常不使用大括号,正如示例 6-3 中的每个分支都只是返回一个值。如果想要在分支中运行多行代码,可以使用大括号。例如,如下代码在每次使用"),s("code",[this._v("Coin::Penny")]),this._v(" 调用时都会打印出 “Lucky penny!”,同时仍然返回代码块最后的值,"),s("code",[this._v("1")]),this._v(":")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Coin "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# Penny"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Nickel"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Dime"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Quarter"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("value_in_cents")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" coin "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Penny "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Lucky penny!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Nickel "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Dime "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Quarter "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("25")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"绑定值的模式"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#绑定值的模式","aria-hidden":"true"}},[this._v("#")]),this._v(" 绑定值的模式")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("作为一个例子,让我们修改枚举的一个成员来存放数据。1999 年到 2008 年间,美帝在 25 美分的硬币的一侧为 50 个州的每一个都印刷了不同的设计。其他的硬币都没有这种区分州的设计,所以只有这些 25 美分硬币有特殊的价值。可以将这些信息加入我们的 "),s("code",[this._v("enum")]),this._v(",通过改变 "),s("code",[this._v("Quarter")]),this._v(" 成员来包含一个 "),s("code",[this._v("State")]),this._v(" 值,示例 6-4 中完成了这些修改:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// 这样可以可以立刻看到州的名称")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" UsState "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Alabama"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Alaska"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Coin "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Penny"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Nickel"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Dime"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Quarter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UsState"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 6-4:"),s("code",[this._v("Quarter")]),this._v(" 成员也存放了一个 "),s("code",[this._v("UsState")]),this._v(" 值的 "),s("code",[this._v("Coin")]),this._v(" 枚举")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在这些代码的匹配表达式中,我们在匹配 "),n("code",[t._v("Coin::Quarter")]),t._v(" 成员的分支的模式中增加了一个叫做 "),n("code",[t._v("state")]),t._v(" 的变量。当匹配到 "),n("code",[t._v("Coin::Quarter")]),t._v(" 时,变量 "),n("code",[t._v("state")]),t._v(" 将会绑定 25 美分硬币所对应州的值。接着在那个分支的代码中使用 "),n("code",[t._v("state")]),t._v(",如下:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" UsState "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# Alabama"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Alaska"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Coin "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# Penny"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Nickel"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Dime"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("Quarter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UsState"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("value_in_cents")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" coin "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Penny "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Nickel "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Dime "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Coin"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("Quarter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("state"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"State quarter from {:?}!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" state"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token number"}},[t._v("25")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如果调用 "),n("code",[t._v("value_in_cents(Coin::Quarter(UsState::Alaska))")]),t._v(","),n("code",[t._v("coin")]),t._v(" 将是 "),n("code",[t._v("Coin::Quarter(UsState::Alaska)")]),t._v("。当将值与每个分支相比较时,没有分支会匹配,直到遇到 "),n("code",[t._v("Coin::Quarter(state)")]),t._v("。这时,"),n("code",[t._v("state")]),t._v(" 绑定的将会是值 "),n("code",[t._v("UsState::Alaska")]),t._v("。接着就可以在 "),n("code",[t._v("println!")]),t._v(" 表达式中使用这个绑定了,像这样就可以获取 "),n("code",[t._v("Coin")]),t._v(" 枚举的 "),n("code",[t._v("Quarter")]),t._v(" 成员中内部的州的值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"匹配-option-t"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#匹配-option-t","aria-hidden":"true"}},[this._v("#")]),this._v(" 匹配 "),s("code",[this._v("Option")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们在之前的部分中使用 "),n("code",[t._v("Option")]),t._v(" 时,是为了从 "),n("code",[t._v("Some")]),t._v(" 中取出其内部的 "),n("code",[t._v("T")]),t._v(" 值;我们还可以像处理 "),n("code",[t._v("Coin")]),t._v(" 枚举那样使用 "),n("code",[t._v("match")]),t._v(" 处理 "),n("code",[t._v("Option")]),t._v("!与其直接比较硬币,我们将比较 "),n("code",[t._v("Option")]),t._v(" 的成员,不过 "),n("code",[t._v("match")]),t._v(" 表达式的工作方式保持不变。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("比如我们想要编写一个函数,它获取一个 "),s("code",[this._v("Option")]),this._v(" 并且如果其中有一个值,将其加一。如果其中没有值,函数应该返回 "),s("code",[this._v("None")]),this._v(" 值并不尝试执行任何操作。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("得益于 "),s("code",[this._v("match")]),this._v(",编写这个函数非常简单,它将看起来像示例 6-5 中这样:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("plus_one")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("i32"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("i32"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" x "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n None "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" None"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" five "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" six "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("plus_one")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("five"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" none "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("plus_one")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("None"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 6-5:一个在 "),s("code",[this._v("Option")]),this._v(" 上使用 "),s("code",[this._v("match")]),this._v(" 表达式的函数")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"匹配-some-t"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#匹配-some-t","aria-hidden":"true"}},[this._v("#")]),this._v(" 匹配 "),s("code",[this._v("Some(T)")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("让我们更仔细地检查 "),n("code",[t._v("plus_one")]),t._v(" 的第一行操作。当调用 "),n("code",[t._v("plus_one(five)")]),t._v(" 时,"),n("code",[t._v("plus_one")]),t._v(" 函数体中的 "),n("code",[t._v("x")]),t._v(" 将会是值 "),n("code",[t._v("Some(5)")]),t._v("。接着将其与每个分支比较。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("None => None,\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("值 "),s("code",[this._v("Some(5)")]),this._v(" 并不匹配模式 "),s("code",[this._v("None")]),this._v(",所以继续进行下一个分支。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("Some(i) => Some(i + 1),\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Some(5)")]),t._v(" 与 "),n("code",[t._v("Some(i)")]),t._v(" 匹配吗?当然匹配!它们是相同的成员。"),n("code",[t._v("i")]),t._v(" 绑定了 "),n("code",[t._v("Some")]),t._v(" 中包含的值,所以 "),n("code",[t._v("i")]),t._v(" 的值是 "),n("code",[t._v("5")]),t._v("。接着匹配分支的代码被执行,所以我们将 "),n("code",[t._v("i")]),t._v(" 的值加一并返回一个含有值 "),n("code",[t._v("6")]),t._v(" 的新 "),n("code",[t._v("Some")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("接着考虑下示例 6-5 中 "),n("code",[t._v("plus_one")]),t._v(" 的第二个调用,这里 "),n("code",[t._v("x")]),t._v(" 是 "),n("code",[t._v("None")]),t._v("。我们进入 "),n("code",[t._v("match")]),t._v(" 并与第一个分支相比较。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("None => None,\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("匹配上了!这里没有值来加一,所以程序结束并返回 "),s("code",[this._v("=>")]),this._v(" 右侧的值 "),s("code",[this._v("None")]),this._v(",因为第一个分支就匹配到了,其他的分支将不再比较。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("将 "),s("code",[this._v("match")]),this._v(" 与枚举相结合在很多场景中都是有用的。你会在 Rust 代码中看到很多这样的模式:"),s("code",[this._v("match")]),this._v(" 一个枚举,绑定其中的值到一个变量,接着根据其值执行代码。这在一开始有点复杂,不过一旦习惯了,你会希望所有语言都拥有它!这一直是用户的最爱。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"匹配是穷尽的"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#匹配是穷尽的","aria-hidden":"true"}},[this._v("#")]),this._v(" 匹配是穷尽的")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("match")]),this._v(" 还有另一方面需要讨论。考虑一下 "),s("code",[this._v("plus_one")]),this._v(" 函数的这个版本,它有一个 bug 并不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn plus_one(x: Option) -> Option {\n match x {\n Some(i) => Some(i + 1),\n }\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们没有处理 "),s("code",[this._v("None")]),this._v(" 的情况,所以这些代码会造成一个 bug。幸运的是,这是一个 Rust 知道如何处理的 bug。如果尝试编译这段代码,会得到这个错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0004]: non-exhaustive patterns: `None` not covered\n --\x3e\n |\n6 | match x {\n | ^ pattern `None` not covered\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("Rust 知道我们没有覆盖所有可能的情况甚至知道哪些模式被忘记了!Rust 中的匹配是 "),n("strong",[t._v("穷尽的")]),t._v("("),n("em",[t._v("exhaustive")]),t._v("):必须穷举到最后的可能性来使代码有效。特别的在这个 "),n("code",[t._v("Option")]),t._v(" 的例子中,Rust 防止我们忘记明确的处理 "),n("code",[t._v("None")]),t._v(" 的情况,这使我们免于假设拥有一个实际上为空的值,这造成了之前提到过的价值亿万的错误。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"通配符"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#通配符","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("_")]),this._v(" 通配符")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 也提供了一个模式用于不想列举出所有可能值的场景。例如,"),s("code",[this._v("u8")]),this._v(" 可以拥有 0 到 255 的有效的值,如果我们只关心 1、3、5 和 7 这几个值,就并不想必须列出 0、2、4、6、8、9 一直到 255 的值。所幸我们不必这么做:可以使用特殊的模式 "),s("code",[this._v("_")]),this._v(" 替代:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" some_u8_value "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0u8")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" some_u8_value "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"one"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token number"}},[t._v("3")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"three"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token number"}},[t._v("5")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"five"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token number"}},[t._v("7")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"seven"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _ "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("_")]),t._v(" 模式会匹配所有的值。通过将其放置于其他分支之后,"),n("code",[t._v("_")]),t._v(" 将会匹配所有之前没有指定的可能的值。"),n("code",[t._v("()")]),t._v(" 就是 unit 值,所以 "),n("code",[t._v("_")]),t._v(" 的情况什么也不会发生。因此,可以说我们想要对 "),n("code",[t._v("_")]),t._v(" 通配符之前没有列出的所有可能的值不做任何处理。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("然而,"),s("code",[this._v("match")]),this._v(" 在只关心 "),s("strong",[this._v("一个")]),this._v(" 情况的场景中可能就有点啰嗦了。为此 Rust 提供了"),s("code",[this._v("if let")]),this._v("。")])}],!1,null,null,null);e.options.__file="ch06-02-match.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/36.572a9496.js b/src/.vuepress/dist/assets/js/36.572a9496.js
new file mode 100644
index 0000000..ff75d35
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/36.572a9496.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[36],{234:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch06-03-if-let.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch06-03-if-let.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),a("p",[t._v("你的 Rust 程序现在能够使用结构体和枚举在自己的作用域内表现其内容了。在你的 API 中使用自定义类型保证了类型安全:编译器会确保你的函数只会得到它期望的类型的值。")]),t._v(" "),a("p",[t._v("为了向你的用户提供一个组织良好的 API,它使用起来很直观并且只向用户暴露他们确实需要的部分,那么现在就让我们转向 Rust 的模块系统吧。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"if-let-简单控制流"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#if-let-简单控制流","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("if let")]),this._v(" 简单控制流")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("if let")]),t._v(" 语法让我们以一种不那么冗长的方式结合 "),a("code",[t._v("if")]),t._v(" 和 "),a("code",[t._v("let")]),t._v(",来处理只匹配一个模式的值而忽略其他模式的情况。考虑示例 6-6 中的程序,它匹配一个 "),a("code",[t._v("Option")]),t._v(" 值并只希望当值为 3 时执行代码:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" some_u8_value "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("Some")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("0u8")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" some_u8_value "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("Some")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"three"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _ "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 6-6:"),s("code",[this._v("match")]),this._v(" 只关心当值为 "),s("code",[this._v("Some(3)")]),this._v(" 时执行代码")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("我们想要对 "),a("code",[t._v("Some(3)")]),t._v(" 匹配进行操作但是不想处理任何其他 "),a("code",[t._v("Some")]),t._v(" 值或 "),a("code",[t._v("None")]),t._v(" 值。为了满足 "),a("code",[t._v("match")]),t._v(" 表达式(穷尽性)的要求,必须在处理完这唯一的成员后加上 "),a("code",[t._v("_ => ()")]),t._v(",这样也要增加很多样板代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不过我们可以使用 "),s("code",[this._v("if let")]),this._v(" 这种更短的方式编写。如下代码与示例 6-6 中的 "),s("code",[this._v("match")]),this._v(" 行为一致:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" some_u8_value "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("Some")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("0u8")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("Some")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" some_u8_value "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"three"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("if let")]),this._v(" 获取通过等号分隔的一个模式和一个表达式。它的工作方式与 "),s("code",[this._v("match")]),this._v(" 相同,这里的表达式对应 "),s("code",[this._v("match")]),this._v(" 而模式则对应第一个分支。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("使用 "),a("code",[t._v("if let")]),t._v(" 意味着编写更少代码,更少的缩进和更少的样板代码。然而,这样会失去 "),a("code",[t._v("match")]),t._v(" 强制要求的穷尽性检查。"),a("code",[t._v("match")]),t._v(" 和 "),a("code",[t._v("if let")]),t._v(" 之间的选择依赖特定的环境以及增加简洁度和失去穷尽性检查的权衡取舍。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("换句话说,可以认为 "),s("code",[this._v("if let")]),this._v(" 是 "),s("code",[this._v("match")]),this._v(" 的一个语法糖,它当值匹配某一模式时执行代码而忽略所有其他值。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("可以在 "),a("code",[t._v("if let")]),t._v(" 中包含一个 "),a("code",[t._v("else")]),t._v("。"),a("code",[t._v("else")]),t._v(" 块中的代码与 "),a("code",[t._v("match")]),t._v(" 表达式中的 "),a("code",[t._v("_")]),t._v(" 分支块中的代码相同,这样的 "),a("code",[t._v("match")]),t._v(" 表达式就等同于 "),a("code",[t._v("if let")]),t._v(" 和 "),a("code",[t._v("else")]),t._v("。回忆一下示例 6-4 中 "),a("code",[t._v("Coin")]),t._v(" 枚举的定义,其 "),a("code",[t._v("Quarter")]),t._v(" 成员也包含一个 "),a("code",[t._v("UsState")]),t._v(" 值。如果想要计数所有不是 25 美分的硬币的同时也报告 25 美分硬币所属的州,可以使用这样一个 "),a("code",[t._v("match")]),t._v(" 表达式:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" UsState "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# Alabama"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Alaska"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Coin "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# Penny"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Nickel"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Dime"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token function"}},[t._v("Quarter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UsState"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" coin "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Coin"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Penny"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" count "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" coin "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Coin"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("Quarter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("state"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"State quarter from {:?}!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" state"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n _ "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" count "),a("span",{attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("或者可以使用这样的 "),s("code",[this._v("if let")]),this._v(" 和 "),s("code",[this._v("else")]),this._v(" 表达式:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" UsState "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# Alabama"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Alaska"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Coin "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# Penny"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Nickel"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# Dime"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token function"}},[t._v("Quarter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("UsState"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" coin "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Coin"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Penny"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" count "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" Coin"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("Quarter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("state"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" coin "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"State quarter from {:?}!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" state"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n count "),a("span",{attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果你的程序遇到一个使用 "),s("code",[this._v("match")]),this._v(" 表达起来过于啰嗦的逻辑,记住 "),s("code",[this._v("if let")]),this._v(" 也在你的 Rust 工具箱中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在我们涉及到了如何使用枚举来创建有一系列可列举值的自定义类型。我们也展示了标准库的 "),s("code",[this._v("Option")]),this._v(" 类型是如何帮助你利用类型系统来避免出错的。当枚举值包含数据时,你可以根据需要处理多少情况来选择使用 "),s("code",[this._v("match")]),this._v(" 或 "),s("code",[this._v("if let")]),this._v(" 来获取并使用这些值。")])}],!1,null,null,null);e.options.__file="ch06-03-if-let.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/37.319db1b7.js b/src/.vuepress/dist/assets/js/37.319db1b7.js
new file mode 100644
index 0000000..a7c8d5d
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/37.319db1b7.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[37],{233:function(e,t,s){"use strict";s.r(t);var r=s(0),_=Object(r.a)({},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("div",{staticClass:"content"},[e._m(0),e._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch07-00-modules.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ch07-00-packages-crates-and-modules.md"),s("OutboundLink")],1),e._v(" "),s("br"),e._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),e._v(" "),e._m(1),e._v(" "),s("p",[e._v("Rust 有一系列与作用域相关的功能。这有时被称为 “模块系统”(“the module system”),不过又不仅仅是模块:")]),e._v(" "),e._m(2),e._v(" "),s("p",[e._v("本章将会覆盖所有这些概念。很快我们就能像专家一样将命名引入作用域、定义作用域和将命名导出到作用域!")])])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"包、crate-与-模块"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#包、crate-与-模块","aria-hidden":"true"}},[this._v("#")]),this._v(" 包、crate 与 模块")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("编写程序时一个核心的问题是 "),t("strong",[this._v("作用域")]),this._v("("),t("em",[this._v("scope")]),this._v("):在代码的某处编译器知道哪些变量名?允许调用哪些函数?这些变量引用的又是什么?")])},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("ul",[s("li",[s("strong",[e._v("包")]),e._v("("),s("em",[e._v("Packages")]),e._v(")是 Cargo 的一个功能,它允许你构建、测试核分享 crate。")]),e._v(" "),s("li",[s("em",[e._v("Crates")]),e._v(" 是一个模块的树形结构,它形成了库或二进制项目。")]),e._v(" "),s("li",[s("strong",[e._v("模块")]),e._v("("),s("em",[e._v("Modules")]),e._v(")和 "),s("em",[e._v("use")]),e._v(" 关键字允许你控制作用域和路径的私有性。")]),e._v(" "),s("li",[s("strong",[e._v("路径")]),e._v("("),s("em",[e._v("path")]),e._v(")是一个命名例如结构体、函数或模块等项的方式")])])}],!1,null,null,null);_.options.__file="ch07-00-packages-crates-and-modules.md.md";t.default=_.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/38.19837e80.js b/src/.vuepress/dist/assets/js/38.19837e80.js
new file mode 100644
index 0000000..7e0a1ec
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/38.19837e80.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[38],{232:function(t,s,e){"use strict";e.r(s);var n=e(0),a=Object(n.a)({},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[t._m(0),t._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch07-01-mod-and-the-filesystem.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch07-01-mod-and-the-filesystem.md"),e("OutboundLink")],1),t._v(" "),e("br"),t._v("\ncommit a120c730714e07f8f32d905e9374a50b2e0ffdf5")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),e("p",[t._v("我们将学习根据编写代码的意图来以不同方法组织库项目代码以适应多种情况。")]),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),e("p",[t._v("而这是示例 7-2 中例子的层次结构:")]),t._v(" "),t._m(26),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._m(49),t._v(" "),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),t._m(56),t._v(" "),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._m(60),t._v(" "),t._m(61),t._v(" "),t._m(62),t._m(63),t._v(" "),t._m(64),t._m(65),t._v(" "),t._m(66),t._v(" "),e("p",[t._v("示例 7-5 中间的 note 事实上是非常有帮助的,因为它指出了一些我们还未讲到的操作:")]),t._v(" "),t._m(67),e("p",[t._v("我们可以按照记录所建议的去操作,而不是继续使用之前的与模块同名文件的模式:")]),t._v(" "),t._m(68),t._v(" "),e("p",[t._v("如下是执行这些步骤的命令:")]),t._v(" "),t._m(69),t._m(70),t._v(" "),t._m(71),e("p",[t._v("对应的文件布局现在看起来像这样:")]),t._v(" "),t._m(72),t._m(73),t._v(" "),t._m(74),t._m(75),t._v(" "),t._m(76),t._v(" "),t._m(77),t._v(" "),e("p",[t._v("让我们总结一下与文件有关的模块规则:")]),t._v(" "),t._m(78),t._v(" "),t._m(79),t._v(" "),t._m(80),t._m(81),t._v(" "),t._m(82)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"mod-和文件系统"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#mod-和文件系统","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("mod")]),this._v(" 和文件系统")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们将通过使用 Cargo 创建一个新项目来开始我们的模块之旅,不过这次不再创建一个二进制 crate,而是创建一个库 crate:一个其他人可以作为依赖导入的项目。第二章猜猜看游戏中作为依赖使用的 "),s("code",[this._v("rand")]),this._v(" 就是这样的 crate。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们将创建一个库的框架,提供一些通用的网络功能;我们将专注于模块和函数的组织,而不必担心函数体中的具体代码。这个项目叫做 "),s("code",[this._v("communicator")]),this._v("。若要创建一个库,应当使用 "),s("code",[this._v("--lib")]),this._v(" 参数而不是之前所用的 "),s("code",[this._v("--bin")]),this._v(" 参数:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo new communicator --lib\n$ cd communicator\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("注意 Cargo 生成了 "),s("em",[this._v("src/lib.rs")]),this._v(" 而不是 "),s("em",[this._v("src/main.rs")]),this._v("。在 "),s("em",[this._v("src/lib.rs")]),this._v(" 中我们会找到这些:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("it_works")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("2")]),t._v(" "),e("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("2")]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("4")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("Cargo 创建了一个空的测试来帮助我们开始库项目,不像使用 "),e("code",[t._v("--bin")]),t._v(" 参数那样创建一个 “Hello, world!” 二进制项目。在本章之后的 “使用 "),e("code",[t._v("super")]),t._v(" 访问父模块” 部分会介绍 "),e("code",[t._v("#[]")]),t._v(" 和 "),e("code",[t._v("mod tests")]),t._v(" 语法,目前只需确保它们位于 "),e("em",[t._v("src/lib.rs")]),t._v(" 底部即可。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为没有 "),s("em",[this._v("src/main.rs")]),this._v(" 文件,所以没有可供 Cargo 的 "),s("code",[this._v("cargo run")]),this._v(" 执行的东西。因此,我们将只使用 "),s("code",[this._v("cargo build")]),this._v(" 命令编译库 crate 的代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"模块定义"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#模块定义","aria-hidden":"true"}},[this._v("#")]),this._v(" 模块定义")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("对于 "),e("code",[t._v("communicator")]),t._v(" 网络库,首先要定义一个叫做 "),e("code",[t._v("network")]),t._v(" 的模块,它包含一个叫做 "),e("code",[t._v("connect")]),t._v(" 的函数定义。Rust 中所有模块的定义都以关键字 "),e("code",[t._v("mod")]),t._v(" 开始。在 "),e("em",[t._v("src/lib.rs")]),t._v(" 文件的开头在测试代码的上面增加这些代码:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" network "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[e("code",[t._v("mod")]),t._v(" 关键字的后面是模块的名字,"),e("code",[t._v("network")]),t._v(",接着是位于大括号中的代码块。代码块中的一切都位于 "),e("code",[t._v("network")]),t._v(" 命名空间中。在这个例子中,只有一个函数,"),e("code",[t._v("connect")]),t._v("。如果想要在 "),e("code",[t._v("network")]),t._v(" 模块外面的代码中调用这个函数,需要指定模块名并使用命名空间语法 "),e("code",[t._v("::")]),t._v(",像这样:"),e("code",[t._v("network::connect()")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("也可以在 "),s("em",[this._v("src/lib.rs")]),this._v(" 文件中同时存在多个模块。例如,再拥有一个 "),s("code",[this._v("client")]),this._v(" 模块,它也有一个叫做 "),s("code",[this._v("connect")]),this._v(" 的函数,如示例 7-1 中所示那样增加这个模块:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" network "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" client "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-1:"),s("code",[this._v("network")]),this._v(" 模块和 "),s("code",[this._v("client")]),this._v(" 一同定义于 "),s("em",[this._v("src/lib.rs")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在我们有了 "),s("code",[this._v("network::connect")]),this._v(" 函数和 "),s("code",[this._v("client::connect")]),this._v(" 函数。它们可能有着完全不同的功能,同时它们也不会彼此冲突,因为它们位于不同的模块。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("在这个例子中,因为我们构建的是一个库,作为库入口点的文件是 "),e("em",[t._v("src/lib.rs")]),t._v("。然而,对于创建模块来说,"),e("em",[t._v("src/lib.rs")]),t._v(" 并没有什么特殊意义。也可以在二进制 crate 的 "),e("em",[t._v("src/main.rs")]),t._v(" 中创建模块,正如在库 crate 的 "),e("em",[t._v("src/lib.rs")]),t._v(" 创建模块一样。事实上,也可以将模块放入其他模块中。这有助于随着模块的增长,将相关的功能组织在一起并又保持各自独立。选择组织代码的方式取决于如何考虑代码各部分之间的关系。例如,对于库的用户来说,"),e("code",[t._v("client")]),t._v(" 模块和它的函数 "),e("code",[t._v("connect")]),t._v(" 可能放在 "),e("code",[t._v("network")]),t._v(" 命名空间里显得更有道理,如示例 7-2 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" network "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" client "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-2:将 "),s("code",[this._v("client")]),this._v(" 模块移动到 "),s("code",[this._v("network")]),this._v(" 模块中")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("在 "),e("em",[t._v("src/lib.rs")]),t._v(" 文件中,将现有的 "),e("code",[t._v("mod network")]),t._v(" 和 "),e("code",[t._v("mod client")]),t._v(" 的定义替换为示例 7-2 中的定义,这里将 "),e("code",[t._v("client")]),t._v(" 模块作为 "),e("code",[t._v("network")]),t._v(" 的一个内部模块。现在我们有了 "),e("code",[t._v("network::connect")]),t._v(" 和 "),e("code",[t._v("network::client::connect")]),t._v(" 函数:它们都叫 "),e("code",[t._v("connect")]),t._v(" ,但它们并不互相冲突,因为它们在不同的命名空间中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这样,模块之间形成了一个层次结构。"),s("em",[this._v("src/lib.rs")]),this._v(" 的内容位于最顶层,而其子模块位于较低的层次。如下是示例 7-1 中的例子以层次的方式考虑的结构:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("communicator\n ├── network\n └── client\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("communicator\n └── network\n └── client\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以看到示例 7-2 中,"),s("code",[this._v("client")]),this._v(" 是 "),s("code",[this._v("network")]),this._v(" 的子模块,而不是它的同级模块。更为复杂的项目可以有很多的模块,所以它们需要符合逻辑地组合在一起以便记录它们。在项目中 “符合逻辑” 的意义全凭你的理解和库的用户对你项目领域的认识。利用我们这里讲到的技术来创建同级模块和嵌套的模块,总有一个会是你会喜欢的结构。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"将模块移动到其他文件"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#将模块移动到其他文件","aria-hidden":"true"}},[this._v("#")]),this._v(" 将模块移动到其他文件")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("位于层级结构中的模块,非常类似计算机领域的另一个我们非常熟悉的结构:文件系统!我们可以利用 Rust 的模块系统连同多个文件一起分解 Rust 项目,这样就不会是所有的内容都落到 "),s("em",[this._v("src/lib.rs")]),this._v(" 或 "),s("em",[this._v("src/main.rs")]),this._v(" 中了。为了举例,我们将从示例 7-3 中的代码开始:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" client "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" network "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" server "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[e("span",{staticClass:"caption"},[t._v("示例 7-3:三个模块,"),e("code",[t._v("client")]),t._v("、"),e("code",[t._v("network")]),t._v(" 和 "),e("code",[t._v("network::server")]),t._v(",它们都定义于 "),e("em",[t._v("src/lib.rs")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("em",[this._v("src/lib.rs")]),this._v(" 文件有如下层次结构:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("communicator\n ├── client\n └── network\n └── server\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("如果这些模块有很多函数,而这些函数又很长,将难以在文件中寻找我们需要的代码。因为这些函数被嵌套进一个或多个 "),e("code",[t._v("mod")]),t._v(" 块中,同时函数中的代码也会开始变长。这就有充分的理由将 "),e("code",[t._v("client")]),t._v("、"),e("code",[t._v("network")]),t._v(" 和 "),e("code",[t._v("server")]),t._v(" 每一个模块从 "),e("em",[t._v("src/lib.rs")]),t._v(" 抽出并放入它们自己的文件中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("首先,将 "),s("code",[this._v("client")]),this._v(" 模块的代码替换为只有 "),s("code",[this._v("client")]),this._v(" 模块声明,这样 "),s("em",[this._v("src/lib.rs")]),this._v(" 看起来应该像如示例 7-4 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("mod client;\n\nmod network {\n fn connect() {\n }\n\n mod server {\n fn connect() {\n }\n }\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-4:提取出 "),s("code",[this._v("client")]),this._v(" 模块的内容但仍将其声明留在 "),s("em",[this._v("src/lib.rs")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("这里我们仍然 "),e("strong",[t._v("声明")]),t._v(" 了 "),e("code",[t._v("client")]),t._v(" 模块,不过将代码块替换为了分号,这告诉了 Rust 在 "),e("code",[t._v("client")]),t._v(" 模块的作用域中寻找另一个定义代码的位置。换句话说,"),e("code",[t._v("mod client;")]),t._v(" 行意味着:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("mod client {\n // contents of client.rs\n}\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("那么现在需要创建对应模块名的外部文件。在 "),e("em",[t._v("src/")]),t._v(" 目录创建一个 "),e("em",[t._v("client.rs")]),t._v(" 文件,接着打开它并输入如下内容,它是上一步被去掉的 "),e("code",[t._v("client")]),t._v(" 模块中的 "),e("code",[t._v("connect")]),t._v(" 函数:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/client.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("注意这个文件中并不需要一个 "),e("code",[t._v("mod")]),t._v(" 声明;因为已经在 "),e("em",[t._v("src/lib.rs")]),t._v(" 中已经使用 "),e("code",[t._v("mod")]),t._v(" 声明了 "),e("code",[t._v("client")]),t._v(" 模块。这个文件仅仅提供 "),e("code",[t._v("client")]),t._v(" 模块的 "),e("strong",[t._v("内容")]),t._v("。如果在这里加上一个 "),e("code",[t._v("mod client")]),t._v(",那么就等于给 "),e("code",[t._v("client")]),t._v(" 模块增加了一个叫做 "),e("code",[t._v("client")]),t._v(" 的子模块了!")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("Rust 默认只知道 "),e("em",[t._v("src/lib.rs")]),t._v(" 中的内容。如果想要对项目加入更多文件,我们需要在 "),e("em",[t._v("src/lib.rs")]),t._v(" 中告诉 Rust 去寻找其他文件;这就是为什么 "),e("code",[t._v("mod client")]),t._v(" 需要被定义在 "),e("em",[t._v("src/lib.rs")]),t._v(" 而不能在 "),e("em",[t._v("src/client.rs")]),t._v(" 的原因。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在,一切应该能成功编译,虽然会有一些警告。记住使用 "),s("code",[this._v("cargo build")]),this._v(" 而不是 "),s("code",[this._v("cargo run")]),this._v(", 因为这是一个库 crate 而不是二进制 crate:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo build\n Compiling communicator v0.1.0 (file:///projects/communicator)\nwarning: function is never used: `connect`\n --\x3e src/client.rs:1:1\n |\n1 | / fn connect() {\n2 | | }\n | |_^\n |\n = note: #[warn(dead_code)] on by default\n\nwarning: function is never used: `connect`\n --\x3e src/lib.rs:4:5\n |\n4 | / fn connect() {\n5 | | }\n | |_____^\n\nwarning: function is never used: `connect`\n --\x3e src/lib.rs:8:9\n |\n8 | / fn connect() {\n9 | | }\n | |_________^\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这些警告提醒我们有从未被使用的函数。目前不用担心这些警告,在本章后面的 “使用 "),s("code",[this._v("pub")]),this._v(" 控制可见性” 部分会解决它们。好消息是,它们仅仅是警告,我们的项目能够成功编译。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("下面使用相同的模式将 "),s("code",[this._v("network")]),this._v(" 模块提取到自己的文件中。删除 "),s("em",[this._v("src/lib.rs")]),this._v(" 中 "),s("code",[this._v("network")]),this._v(" 模块的内容并在声明后加上一个分号,像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("mod client;\n\nmod network;\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接着新建 "),s("em",[this._v("src/network.rs")]),this._v(" 文件并输入如下内容:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/network.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" server "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("注意这个模块文件中我们也使用了一个 "),s("code",[this._v("mod")]),this._v(" 声明;这是因为我们希望 "),s("code",[this._v("server")]),this._v(" 成为 "),s("code",[this._v("network")]),this._v(" 的一个子模块。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("现在再次运行 "),e("code",[t._v("cargo build")]),t._v("。成功!不过我们还需要再提取出另一个模块:"),e("code",[t._v("server")]),t._v("。因为这是一个子模块——也就是模块中的模块——目前的将模块提取到对应名字的文件中的策略就不管用了。如果我们仍这么尝试则会出现错误。对 "),e("em",[t._v("src/network.rs")]),t._v(" 的第一个修改是用 "),e("code",[t._v("mod server;")]),t._v(" 替换 "),e("code",[t._v("server")]),t._v(" 模块的内容:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/network.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn connect() {\n}\n\nmod server;\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接着创建 "),s("em",[this._v("src/server.rs")]),this._v(" 文件并输入需要提取的 "),s("code",[this._v("server")]),this._v(" 模块的内容:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/server.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("connect")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当尝试运行 "),s("code",[this._v("cargo build")]),this._v(" 时,会出现如示例 7-5 中所示的错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo build\n Compiling communicator v0.1.0 (file:///projects/communicator)\nerror: cannot declare a new module at this location\n --\x3e src/network.rs:4:5\n |\n4 | mod server;\n | ^^^^^^\n |\nnote: maybe move this module `src/network.rs` to its own directory via `src/network/mod.rs`\n --\x3e src/network.rs:4:5\n |\n4 | mod server;\n | ^^^^^^\nnote: ... or maybe `use` the module `server` instead of possibly redeclaring it\n --\x3e src/network.rs:4:5\n |\n4 | mod server;\n | ^^^^^^\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-5:尝试将 "),s("code",[this._v("server")]),this._v(" 子模块提取到 "),s("em",[this._v("src/server.rs")]),this._v(" 时出现的错误")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("这个错误说明 “不能在这个位置新声明一个模块” 并指出 "),e("em",[t._v("src/network.rs")]),t._v(" 中的 "),e("code",[t._v("mod server;")]),t._v(" 这一行。看来 "),e("em",[t._v("src/network.rs")]),t._v(" 与 "),e("em",[t._v("src/lib.rs")]),t._v(" 在某些方面是不同的;继续阅读以理解这是为什么。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("note: maybe move this module `network` to its own directory via\n`network/mod.rs`\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("ol",[e("li",[t._v("新建一个叫做 "),e("em",[t._v("network")]),t._v(" 的 "),e("strong",[t._v("目录")]),t._v(",这是父模块的名字")]),t._v(" "),e("li",[t._v("将 "),e("em",[t._v("src/network.rs")]),t._v(" 移动到新建的 "),e("em",[t._v("network")]),t._v(" 目录中并重命名为 "),e("em",[t._v("src/network/mod.rs")])]),t._v(" "),e("li",[t._v("将子模块文件 "),e("em",[t._v("src/server.rs")]),t._v(" 移动到 "),e("em",[t._v("network")]),t._v(" 目录中")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ mkdir src/network\n$ mv src/network.rs src/network/mod.rs\n$ mv src/server.rs src/network\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在如果运行 "),s("code",[this._v("cargo build")]),this._v(" 的话将顺利编译(虽然仍有警告)。现在模块的布局看起来仍然与示例 7-3 中所有代码都在 "),s("em",[this._v("src/lib.rs")]),this._v(" 中时完全一样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("communicator\n ├── client\n └── network\n └── server\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("└── src\n ├── client.rs\n ├── lib.rs\n └── network\n ├── mod.rs\n └── server.rs\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("那么,当我们想要提取 "),e("code",[t._v("network::server")]),t._v(" 模块时,为什么也必须将 "),e("em",[t._v("src/network.rs")]),t._v(" 文件改名成 "),e("em",[t._v("src/network/mod.rs")]),t._v(" 文件呢,还有为什么要将 "),e("code",[t._v("network::server")]),t._v(" 的代码放入 "),e("em",[t._v("network")]),t._v(" 目录的 "),e("em",[t._v("src/network/server.rs")]),t._v(" 文件中呢?原因是如果 "),e("em",[t._v("server.rs")]),t._v(" 文件在 "),e("em",[t._v("src")]),t._v(" 目录中那么 Rust 就不能知道 "),e("code",[t._v("server")]),t._v(" 应当是 "),e("code",[t._v("network")]),t._v(" 的子模块。为了阐明这里 Rust 的行为,让我们考虑一下有着如下层级的另一个例子,其所有定义都位于 "),e("em",[t._v("src/lib.rs")]),t._v(" 中:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("communicator\n ├── client\n └── network\n └── client\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("在这个例子中,仍然有这三个模块,"),e("code",[t._v("client")]),t._v("、"),e("code",[t._v("network")]),t._v(" 和 "),e("code",[t._v("network::client")]),t._v("。如果按照与上面最开始将模块提取到文件中相同的步骤来操作,对于 "),e("code",[t._v("client")]),t._v(" 模块会创建 "),e("em",[t._v("src/client.rs")]),t._v("。对于 "),e("code",[t._v("network")]),t._v(" 模块,会创建 "),e("em",[t._v("src/network.rs")]),t._v("。但是接下来不能将 "),e("code",[t._v("network::client")]),t._v(" 模块提取到 "),e("em",[t._v("src/client.rs")]),t._v(" 文件中,因为它已经存在了,对应顶层的 "),e("code",[t._v("client")]),t._v(" 模块!如果将 "),e("code",[t._v("client")]),t._v(" 和 "),e("code",[t._v("network::client")]),t._v(" 的代码都放入 "),e("em",[t._v("src/client.rs")]),t._v(" 文件,Rust 将无从可知这些代码是属于 "),e("code",[t._v("client")]),t._v(" 还是 "),e("code",[t._v("network::client")]),t._v(" 的。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("因此,为了将 "),e("code",[t._v("network")]),t._v(" 模块的子模块 "),e("code",[t._v("network::client")]),t._v(" 提取到一个文件中,需要为 "),e("code",[t._v("network")]),t._v(" 模块新建一个目录替代 "),e("em",[t._v("src/network.rs")]),t._v(" 文件。接着 "),e("code",[t._v("network")]),t._v(" 模块的代码将进入 "),e("em",[t._v("src/network/mod.rs")]),t._v(" 文件,而子模块 "),e("code",[t._v("network::client")]),t._v(" 将拥有其自己的文件 "),e("em",[t._v("src/network/client.rs")]),t._v("。现在顶层的 "),e("em",[t._v("src/client.rs")]),t._v(" 中的代码毫无疑问的都属于 "),e("code",[t._v("client")]),t._v(" 模块。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"模块文件系统的规则"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#模块文件系统的规则","aria-hidden":"true"}},[this._v("#")]),this._v(" 模块文件系统的规则")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("ul",[e("li",[t._v("如果一个叫做 "),e("code",[t._v("foo")]),t._v(" 的模块没有子模块,应该将 "),e("code",[t._v("foo")]),t._v(" 的声明放入叫做 "),e("em",[t._v("foo.rs")]),t._v(" 的文件中。")]),t._v(" "),e("li",[t._v("如果一个叫做 "),e("code",[t._v("foo")]),t._v(" 的模块有子模块,应该将 "),e("code",[t._v("foo")]),t._v(" 的声明放入叫做 "),e("em",[t._v("foo/mod.rs")]),t._v(" 的文件中。")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("这些规则适用于递归(嵌套),所以如果 "),e("code",[t._v("foo")]),t._v(" 模块有一个子模块 "),e("code",[t._v("bar")]),t._v(" 而 "),e("code",[t._v("bar")]),t._v(" 没有子模块,则 "),e("em",[t._v("src")]),t._v(" 目录中应该有如下文件:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("└── foo\n ├── bar.rs (contains the declarations in `foo::bar`)\n └── mod.rs (contains the declarations in `foo`, including `mod bar`)\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("模块自身则应该使用 "),s("code",[this._v("mod")]),this._v(" 关键字定义于父模块的文件中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接下来,我们讨论一下 "),s("code",[this._v("pub")]),this._v(" 关键字,并除掉那些警告!")])}],!1,null,null,null);a.options.__file="ch07-01-mod-and-the-filesystem.md";s.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/39.72675c02.js b/src/.vuepress/dist/assets/js/39.72675c02.js
new file mode 100644
index 0000000..683179a
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/39.72675c02.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[39],{231:function(e,t,r){"use strict";r.r(t);var a=r(0),s=Object(a.a)({},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"content"},[e._m(0),e._v(" "),r("blockquote",[r("p",[r("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch07-01-packages-and-crates-for-making-libraries-and-executables.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ch07-01-mod-and-the-filesystem.md"),r("OutboundLink")],1),e._v(" "),r("br"),e._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),e._v(" "),e._m(1),e._v(" "),e._m(2),e._v(" "),e._m(3),e._v(" "),e._m(4),e._m(5),e._v(" "),r("p",[e._v("一个包可以带有零个或一个库 crate 和任意多个二进制 crate。一个包中必须带有至少一个(库或者二进制)crate。")]),e._v(" "),e._m(6),e._v(" "),r("p",[e._v("接下来让我们讨论模块!")])])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"包和-crate-用来创建库和二进制项目"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#包和-crate-用来创建库和二进制项目","aria-hidden":"true"}},[this._v("#")]),this._v(" 包和 crate 用来创建库和二进制项目")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("让我们聊聊 "),t("strong",[this._v("模块")]),this._v(" 与 "),t("em",[this._v("crate")]),this._v("。下面是一个总结:")])},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("ul",[r("li",[r("em",[e._v("crate")]),e._v(" 是一个二进制或库项目。")]),e._v(" "),r("li",[r("strong",[e._v("crate 根")]),e._v("("),r("em",[e._v("crate root")]),e._v(")是一个用来描述如何构建 crate 的文件。")]),e._v(" "),r("li",[r("strong",[e._v("包")]),e._v(" 带有 "),r("em",[e._v("Cargo.toml")]),e._v(" 文件用以描述如何构建一个或多个 crate。一个包中至多可以有一个库项目。")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("所以当运行 "),t("code",[this._v("cargo new")]),this._v(" 时是在创建一个包:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo new my-project\n Created binary (application) `my-project` package\n$ ls my-project\nCargo.toml\nsrc\n$ ls my-project/src\nmain.rs\n")])])])},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("p",[e._v("因为 Cargo 创建了 "),r("em",[e._v("Cargo.toml")]),e._v(",这意味着现在我们有了一个包。如果查看 "),r("em",[e._v("Cargo.toml")]),e._v(" 的内容,会发现并没有提到 "),r("em",[e._v("src/main.rs")]),e._v("。然而,Cargo 的约定是如果在代表包的 "),r("em",[e._v("Cargo.toml")]),e._v(" 的同级目录下包含 "),r("em",[e._v("src")]),e._v(" 目录且其中包含 "),r("em",[e._v("main.rs")]),e._v(" 文件的话,Cargo 就知道这个包带有一个与包同名的二进制 crate,且 "),r("em",[e._v("src/main.rs")]),e._v(" 就是 crate 根。另一个约定如果包目录中包含 "),r("em",[e._v("src/lib.rs")]),e._v(",则包带有与其同名的库 crate,且 "),r("em",[e._v("src/lib.rs")]),e._v(" 是 crate 根。crate 根文件将由 Cargo 传递给 "),r("code",[e._v("rustc")]),e._v(" 来实际构建库或者二进制项目。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("如果包同时包含 "),t("em",[this._v("src/main.rs")]),this._v(" 和 "),t("em",[this._v("src/lib.rs")]),this._v(",那么它带有两个 crate:一个库和一个二进制项目,同名。如果只有其中之一,则包将只有一个库或者二进制 crate。包可以带有多个二进制 crate,需将其文件置于 "),t("em",[this._v("src/bin")]),this._v(" 目录;每个文件将是一个单独的二进制 crate。")])}],!1,null,null,null);s.options.__file="ch07-01-packages-and-crates-for-making-libraries-and-executables.md";t.default=s.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/4.f9a0ae28.js b/src/.vuepress/dist/assets/js/4.f9a0ae28.js
new file mode 100644
index 0000000..8524330
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/4.f9a0ae28.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{251:function(t,r,l){"use strict";l.r(r);var i=l(0),e=Object(i.a)({},function(){var t=this,r=t.$createElement,l=t._self._c||r;return l("div",{staticClass:"content"},[t._m(0),t._v(" "),l("p",[l("router-link",{attrs:{to:"./foreword.html"}},[t._v("前言")]),t._v(" "),l("router-link",{attrs:{to:"./ch00-00-introduction.html"}},[t._v("介绍")])],1),t._v(" "),t._m(1),t._v(" "),l("ul",[l("li",[l("p",[l("router-link",{attrs:{to:"./ch01-00-getting-started.html"}},[t._v("入门指南")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch01-01-installation.html"}},[t._v("安装")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch01-02-hello-world.html"}},[t._v("Hello, World!")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch01-03-hello-cargo.html"}},[t._v("Hello, Cargo!")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch02-00-guessing-game-tutorial.html"}},[t._v("猜猜看游戏教程")])],1)]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch03-00-common-programming-concepts.html"}},[t._v("通用编程概念")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch03-01-variables-and-mutability.html"}},[t._v("变量与可变性")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch03-02-data-types.html"}},[t._v("数据类型")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch03-03-how-functions-work.html"}},[t._v("函数如何工作")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch03-04-comments.html"}},[t._v("注释")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch03-05-control-flow.html"}},[t._v("控制流")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch04-00-understanding-ownership.html"}},[t._v("认识所有权")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch04-01-what-is-ownership.html"}},[t._v("什么是所有权?")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch04-02-references-and-borrowing.html"}},[t._v("引用与借用")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch04-03-slices.html"}},[t._v("Slices")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch05-00-structs.html"}},[t._v("使用结构体来组织相关联的数据")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch05-01-defining-structs.html"}},[t._v("定义并实例化结构体")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch05-02-example-structs.html"}},[t._v("一个使用结构体的示例程序")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch05-03-method-syntax.html"}},[t._v("方法语法")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch06-00-enums.html"}},[t._v("枚举与模式匹配")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch06-01-defining-an-enum.html"}},[t._v("定义枚举")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch06-02-match.html"}},[l("code",[t._v("match")]),t._v(" 控制流运算符")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch06-03-if-let.html"}},[l("code",[t._v("if let")]),t._v(" 简洁控制流")])],1)])])]),t._v(" "),t._m(2),t._v(" "),l("ul",[l("li",[l("p",[l("router-link",{attrs:{to:"./ch07-00-packages-crates-and-modules.html"}},[t._v("包、crate 与 模块")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch07-01-packages-and-crates-for-making-libraries-and-executables.html"}},[t._v("包和 crate 用来创建库和二进制项目")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch07-02-modules-and-use-to-control-scope-and-privacy.html"}},[t._v("模块系统用来控制作用域和私有性")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch08-00-common-collections.html"}},[t._v("通用集合类型")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch08-01-vectors.html"}},[t._v("vector")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch08-02-strings.html"}},[t._v("字符串")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch08-03-hash-maps.html"}},[t._v("哈希 map")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch09-00-error-handling.html"}},[t._v("错误处理")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch09-01-unrecoverable-errors-with-panic.html"}},[l("code",[t._v("panic!")]),t._v(" 与不可恢复的错误")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch09-02-recoverable-errors-with-result.html"}},[l("code",[t._v("Result")]),t._v(" 与可恢复的错误")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch09-03-to-panic-or-not-to-panic.html"}},[l("code",[t._v("panic!")]),t._v(" 还是不 "),l("code",[t._v("panic!")])])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch10-00-generics.html"}},[t._v("泛型、trait 与生命周期")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch10-01-syntax.html"}},[t._v("泛型数据类型")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch10-02-traits.html"}},[t._v("trait:定义共享的行为")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch10-03-lifetime-syntax.html"}},[t._v("生命周期与引用有效性")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch11-00-testing.html"}},[t._v("测试")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch11-01-writing-tests.html"}},[t._v("编写测试")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch11-02-running-tests.html"}},[t._v("运行测试")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch11-03-test-organization.html"}},[t._v("测试的组织结构")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch12-00-an-io-project.html"}},[t._v("一个 I/O 项目:构建命令行程序")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch12-01-accepting-command-line-arguments.html"}},[t._v("接受命令行参数")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch12-02-reading-a-file.html"}},[t._v("读取文件")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch12-03-improving-error-handling-and-modularity.html"}},[t._v("重构以改进模块化与错误处理")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch12-04-testing-the-librarys-functionality.html"}},[t._v("采用测试驱动开发完善库的功能")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch12-05-working-with-environment-variables.html"}},[t._v("处理环境变量")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch12-06-writing-to-stderr-instead-of-stdout.html"}},[t._v("将错误信息输出到标准错误而不是标准输出")])],1)])])]),t._v(" "),t._m(3),t._v(" "),l("ul",[l("li",[l("p",[l("router-link",{attrs:{to:"./ch13-00-functional-features.html"}},[t._v("Rust 中的函数式语言功能:迭代器与闭包")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch13-01-closures.html"}},[t._v("闭包:可以捕获其环境的匿名函数")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch13-02-iterators.html"}},[t._v("使用迭代器处理元素序列")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch13-03-improving-our-io-project.html"}},[t._v("改进之前的 I/O 项目")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch13-04-performance.html"}},[t._v("性能比较:循环对迭代器")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch14-00-more-about-cargo.html"}},[t._v("更多关于 Cargo 和 Crates.io 的内容")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch14-01-release-profiles.html"}},[t._v("采用发布配置自定义构建")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch14-02-publishing-to-crates-io.html"}},[t._v("将 crate 发布到 Crates.io")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch14-03-cargo-workspaces.html"}},[t._v("Cargo 工作空间")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch14-04-installing-binaries.html"}},[t._v("使用 "),l("code",[t._v("cargo install")]),t._v(" 从 Crates.io 安装二进制文件")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch14-05-extending-cargo.html"}},[t._v("Cargo 自定义扩展命令")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch15-00-smart-pointers.html"}},[t._v("智能指针")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch15-01-box.html"}},[l("code",[t._v("Box")]),t._v(" 指向堆上数据,并且可确定大小")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch15-02-deref.html"}},[t._v("通过 "),l("code",[t._v("Deref")]),t._v(" trait 将智能指针当作常规引用处理")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch15-03-drop.html"}},[l("code",[t._v("Drop")]),t._v(" Trait 运行清理代码")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch15-04-rc.html"}},[l("code",[t._v("Rc")]),t._v(" 引用计数智能指针")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch15-05-interior-mutability.html"}},[l("code",[t._v("RefCell")]),t._v(" 与内部可变性模式")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch15-06-reference-cycles.html"}},[t._v("引用循环与内存泄漏是安全的")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch16-00-concurrency.html"}},[t._v("无畏并发")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch16-01-threads.html"}},[t._v("线程")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch16-02-message-passing.html"}},[t._v("消息传递")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch16-03-shared-state.html"}},[t._v("共享状态")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch16-04-extensible-concurrency-sync-and-send.html"}},[t._v("可扩展的并发:"),l("code",[t._v("Sync")]),t._v(" 与 "),l("code",[t._v("Send")])])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch17-00-oop.html"}},[t._v("Rust 的面向对象编程特性")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch17-01-what-is-oo.html"}},[t._v("面向对象语言的特点")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch17-02-trait-objects.html"}},[t._v("为使用不同类型的值而设计的 trait 对象")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch17-03-oo-design-patterns.html"}},[t._v("面向对象设计模式的实现")])],1)])])]),t._v(" "),t._m(4),t._v(" "),l("ul",[l("li",[l("p",[l("router-link",{attrs:{to:"./ch18-00-patterns.html"}},[t._v("模式用来匹配值的结构")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch18-01-all-the-places-for-patterns.html"}},[t._v("所有可能会用到模式的位置")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch18-02-refutability.html"}},[t._v("Refutability:何时模式可能会匹配失败")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch18-03-pattern-syntax.html"}},[t._v("模式的全部语法")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch19-00-advanced-features.html"}},[t._v("高级特征")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch19-01-unsafe-rust.html"}},[t._v("不安全的 Rust")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch19-02-advanced-lifetimes.html"}},[t._v("高级生命周期")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch19-03-advanced-traits.html"}},[t._v("高级 trait")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch19-04-advanced-types.html"}},[t._v("高级类型")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch19-05-advanced-functions-and-closures.html"}},[t._v("高级函数与闭包")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./ch20-00-final-project-a-web-server.html"}},[t._v("最后的项目: 构建多线程 web server")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./ch20-01-single-threaded.html"}},[t._v("单线程 web server")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch20-02-multithreaded.html"}},[t._v("将单线程 server 变为多线程 server")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./ch20-03-graceful-shutdown-and-cleanup.html"}},[t._v("优雅停机与清理")])],1)])]),t._v(" "),l("li",[l("p",[l("router-link",{attrs:{to:"./appendix-00.html"}},[t._v("附录")])],1),t._v(" "),l("ul",[l("li",[l("router-link",{attrs:{to:"./appendix-01-keywords.html"}},[t._v("A - 关键字")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./appendix-02-operators.html"}},[t._v("B - 运算符与符号")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./appendix-03-derivable-traits.html"}},[t._v("C - 可派生的 trait")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./appendix-04-macros.html"}},[t._v("D - 宏")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./appendix-05-translation.html"}},[t._v("E - 本书翻译")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./appendix-06-newest-features.html"}},[t._v("F - 最新功能")])],1),t._v(" "),l("li",[l("router-link",{attrs:{to:"./appendix-07-nightly-rust.html"}},[t._v("G - Rust 是如何开发的与 “Nightly Rust”")])],1)])])])])},[function(){var t=this.$createElement,r=this._self._c||t;return r("h1",{attrs:{id:"rust-程序设计语言"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#rust-程序设计语言","aria-hidden":"true"}},[this._v("#")]),this._v(" Rust 程序设计语言")])},function(){var t=this.$createElement,r=this._self._c||t;return r("h2",{attrs:{id:"入门指南"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#入门指南","aria-hidden":"true"}},[this._v("#")]),this._v(" 入门指南")])},function(){var t=this.$createElement,r=this._self._c||t;return r("h2",{attrs:{id:"基本-rust-技能"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#基本-rust-技能","aria-hidden":"true"}},[this._v("#")]),this._v(" 基本 Rust 技能")])},function(){var t=this.$createElement,r=this._self._c||t;return r("h2",{attrs:{id:"rust-编程思想"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#rust-编程思想","aria-hidden":"true"}},[this._v("#")]),this._v(" Rust 编程思想")])},function(){var t=this.$createElement,r=this._self._c||t;return r("h2",{attrs:{id:"高级主题"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#高级主题","aria-hidden":"true"}},[this._v("#")]),this._v(" 高级主题")])}],!1,null,null,null);e.options.__file="SUMMARY.md";r.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/40.d9f81c69.js b/src/.vuepress/dist/assets/js/40.d9f81c69.js
new file mode 100644
index 0000000..d12a622
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/40.d9f81c69.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[40],{240:function(t,e,n){"use strict";n.r(e);var s=n(0),r=Object(s.a)({},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch07-02-controlling-visibility-with-pub.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch07-02-controlling-visibility-with-pub.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit a120c730714e07f8f32d905e9374a50b2e0ffdf5")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),n("p",[t._v("并编译代码:")]),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),n("p",[t._v("现在编译的话,那个警告就消失了:")]),t._v(" "),t._m(34),n("p",[t._v("只剩一个警告了!尝试自食其力修改它吧!")]),t._v(" "),t._m(35),t._v(" "),n("p",[t._v("总的来说,有如下可见性规则:")]),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._v(" "),n("p",[t._v("这里有一些尝试修复错误的代码修改意见。在你尝试它们之前,猜测一下它们哪个能修复错误,接着编译查看你是否猜对了,并结合私有性规则理解为什么。")]),t._v(" "),t._m(49),t._v(" "),n("p",[t._v("请随意设计更多的实验并尝试理解它们!")]),t._v(" "),t._m(50)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"使用-pub-控制可见性"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#使用-pub-控制可见性","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),e("code",[this._v("pub")]),this._v(" 控制可见性")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[t._v("我们通过将 "),n("code",[t._v("network")]),t._v(" 和 "),n("code",[t._v("network::server")]),t._v(" 的代码分别移动到 "),n("em",[t._v("src/network/mod.rs")]),t._v(" 和 "),n("em",[t._v("src/network/server.rs")]),t._v(" 文件中解决了示例 7-5 中出现的错误信息。现在,"),n("code",[t._v("cargo build")]),t._v(" 能够构建我们的项目,不过仍然有一些警告信息,表示 "),n("code",[t._v("client::connect")]),t._v("、"),n("code",[t._v("network::connect")]),t._v(" 和"),n("code",[t._v("network::server::connect")]),t._v(" 函数没有被使用:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("warning: function is never used: `connect`\n --\x3e src/client.rs:1:1\n |\n1 | / fn connect() {\n2 | | }\n | |_^\n |\n = note: #[warn(dead_code)] on by default\n\nwarning: function is never used: `connect`\n --\x3e src/network/mod.rs:1:1\n |\n1 | / fn connect() {\n2 | | }\n | |_^\n\nwarning: function is never used: `connect`\n --\x3e src/network/server.rs:1:1\n |\n1 | / fn connect() {\n2 | | }\n | |_^\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("那么为什么会出现这些警告信息呢?毕竟我们构建的是一个库,它的函数的目的是被 "),e("strong",[this._v("用户")]),this._v(" 使用,而不一定要被项目自身使用,所以不应该担心这些 "),e("code",[this._v("connect")]),this._v(" 函数是未使用的。创建它们的意义就在于被另一个项目而不是被我们自己使用。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("为了理解为什么这个程序出现了这些警告,尝试在另一个项目中使用这个 "),e("code",[this._v("connect")]),this._v(" 库,从外部调用它们。为此,通过创建一个包含这些代码的 "),e("em",[this._v("src/main.rs")]),this._v(" 文件,在与库 crate 相同的目录创建一个二进制 crate:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("extern crate communicator;\n\nfn main() {\n communicator::client::connect();\n}\n")])])])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[t._v("使用 "),n("code",[t._v("extern crate")]),t._v(" 指令将 "),n("code",[t._v("communicator")]),t._v(" 库 crate 引入到作用域。我们的包现在包含 "),n("strong",[t._v("两个")]),t._v(" crate。Cargo 认为 "),n("em",[t._v("src/main.rs")]),t._v(" 是一个二进制 crate 的根文件,与现存的以 "),n("em",[t._v("src/lib.rs")]),t._v(" 为根文件的库 crate 相区分。这个模式在可执行项目中非常常见:大部分功能位于库 crate 中,而二进制 crate 使用该库 crate。通过这种方式,其他程序也可以使用这个库 crate,这是一个很好的关注分离(separation of concerns)。")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[t._v("从一个外部 crate 的视角观察 "),n("code",[t._v("communicator")]),t._v(" 库的内部,我们创建的所有模块都位于一个与 crate 同名的模块内部,"),n("code",[t._v("communicator")]),t._v("。这个顶层的模块被称为 crate 的 "),n("strong",[t._v("根模块")]),t._v("("),n("em",[t._v("root module")]),t._v(")。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("另外注意到即便在项目的子模块中使用外部 crate,"),e("code",[this._v("extern crate")]),this._v(" 也应该位于根模块(也就是 "),e("em",[this._v("src/main.rs")]),this._v(" 或 "),e("em",[this._v("src/lib.rs")]),this._v(")。接着,在子模块中,我们就可以像顶层模块那样引用外部 crate 中的项了。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("我们的二进制 crate 如今正好调用了库中 "),e("code",[this._v("client")]),this._v(" 模块的 "),e("code",[this._v("connect")]),this._v(" 函数。然而,执行 "),e("code",[this._v("cargo build")]),this._v(" 会在之前的警告之后出现一个错误:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("error[E0603]: module `client` is private\n --\x3e src/main.rs:4:5\n |\n4 | communicator::client::connect();\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n")])])])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[t._v("啊哈!这告诉了我们 "),n("code",[t._v("client")]),t._v(" 模块是私有的,这也正是那些警告的症结所在。这也是我们第一次在 Rust 上下文中涉及到 "),n("strong",[t._v("公有")]),t._v("("),n("em",[t._v("public")]),t._v(")和 "),n("strong",[t._v("私有")]),t._v("("),n("em",[t._v("private")]),t._v(")的概念。Rust 所有代码的默认状态是私有的:除了自己之外别人不允许使用这些代码。如果不在自己的项目中使用一个私有函数,因为程序自身是唯一允许使用这个函数的代码,Rust 会警告说函数未被使用。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("一旦我们指定一个像 "),e("code",[this._v("client::connect")]),this._v(" 的函数为公有,不光二进制 crate 中的函数调用是允许的,函数未被使用的警告也会消失。将其标记为公有让 Rust 知道了函数将会在程序的外部被使用。现在这个可能的理论上的外部可用性使得 Rust 认为这个函数 “已经被使用”。因此。当某项被标记为公有,Rust 不再要求它在程序自身被使用并停止警告函数未被使用。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"标记函数为公有"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#标记函数为公有","aria-hidden":"true"}},[this._v("#")]),this._v(" 标记函数为公有")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[t._v("为了告诉 Rust 将函数标记为公有,在声明的开头增加 "),n("code",[t._v("pub")]),t._v(" 关键字。现在我们将致力于修复 "),n("code",[t._v("client::connect")]),t._v(" 未被使用的警告,以及二进制 crate 中 “模块 "),n("code",[t._v("client")]),t._v(" 是私有的” 的错误。像这样修改 "),n("em",[t._v("src/lib.rs")]),t._v(" 使 "),n("code",[t._v("client")]),t._v(" 模块公有:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("pub mod client;\n\nmod network;\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("code",[this._v("pub")]),this._v(" 写在 "),e("code",[this._v("mod")]),this._v(" 之前。再次尝试构建:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("error[E0603]: function `connect` is private\n --\x3e src/main.rs:4:5\n |\n4 | communicator::client::connect();\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("非常好!另一个不同的错误!好的,不同的错误信息也是值得庆祝的。新错误表明“函数 "),e("code",[this._v("connect")]),this._v(" 是私有的”,那么让我们修改 "),e("em",[this._v("src/client.rs")]),this._v(" 将 "),e("code",[this._v("client::connect")]),this._v(" 也设为公有:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/client.rs")])])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("connect")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("再一次运行 "),e("code",[this._v("cargo build")]),this._v(":")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("warning: function is never used: `connect`\n --\x3e src/network/mod.rs:1:1\n |\n1 | / fn connect() {\n2 | | }\n | |_^\n |\n = note: #[warn(dead_code)] on by default\n\nwarning: function is never used: `connect`\n --\x3e src/network/server.rs:1:1\n |\n1 | / fn connect() {\n2 | | }\n | |_^\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("编译通过了,关于 "),e("code",[this._v("client::connect")]),this._v(" 未被使用的警告消失了!")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("未被使用的代码并不总是意味着它们需要被设为公有的:如果你 "),e("strong",[this._v("不")]),this._v(" 希望这些函数成为公有 API 的一部分,未被使用的代码警告可能是在提醒你这些代码不再需要并可以安全地删除它们。这也可能是警告你出 bug 了,如果你刚刚不小心删除了库中所有这个函数的调用。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("当然我们的情况是,"),e("strong",[this._v("确实")]),this._v(" 希望另外两个函数也作为 crate 公有 API 的一部分,所以让我们也将其标记为 "),e("code",[this._v("pub")]),this._v(" 并去掉剩余的警告。修改 "),e("em",[this._v("src/network/mod.rs")]),this._v(" 为:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/network/mod.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("pub fn connect() {\n}\n\nmod server;\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("warning: function is never used: `connect`\n --\x3e src/network/mod.rs:1:1\n |\n1 | / pub fn connect() {\n2 | | }\n | |_^\n |\n = note: #[warn(dead_code)] on by default\n\nwarning: function is never used: `connect`\n --\x3e src/network/server.rs:1:1\n |\n1 | / fn connect() {\n2 | | }\n | |_^\n")])])])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[t._v("虽然将 "),n("code",[t._v("network::connect")]),t._v(" 设为 "),n("code",[t._v("pub")]),t._v(" 了,我们仍然得到了一个未被使用函数的警告。这是因为模块中的函数是公有的,不过函数所在的 "),n("code",[t._v("network")]),t._v(" 模块却不是公有的。这回我们是自内向外修改库文件的,而 "),n("code",[t._v("client::connect")]),t._v(" 的时候是自外向内修改的。我们需要修改 "),n("em",[t._v("src/lib.rs")]),t._v(" 让 "),n("code",[t._v("network")]),t._v(" 也是公有的,如下:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("pub mod client;\n\npub mod network;\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("warning: function is never used: `connect`\n --\x3e src/network/server.rs:1:1\n |\n1 | / fn connect() {\n2 | | }\n | |_^\n |\n = note: #[warn(dead_code)] on by default\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"私有性规则"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#私有性规则","aria-hidden":"true"}},[this._v("#")]),this._v(" 私有性规则")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ol",[e("li",[this._v("如果一个项是公有的,它能被任何父模块访问")]),this._v(" "),e("li",[this._v("如果一个项是私有的,它能被其直接父模块及其任何子模块访问")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"私有性示例"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#私有性示例","aria-hidden":"true"}},[this._v("#")]),this._v(" 私有性示例")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("让我们看看更多私有性的例子作为练习。创建一个新的库项目并在新项目的 "),e("em",[this._v("src/lib.rs")]),this._v(" 输入示例 7-6 中的代码:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("mod outermost {\n pub fn middle_function() {}\n\n fn middle_secret_function() {}\n\n mod inside {\n pub fn inner_function() {}\n\n fn secret_function() {}\n }\n}\n\nfn try_me() {\n outermost::middle_function();\n outermost::middle_secret_function();\n outermost::inside::inner_function();\n outermost::inside::secret_function();\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 7-6:私有和公有函数的例子,其中部分是不正确的")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("在尝试编译这些代码之前,猜测一下 "),e("code",[this._v("try_me")]),this._v(" 函数的哪一行会出错。接着编译项目来看看是否猜对了,然后继续阅读后面关于错误的讨论!")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"检查错误"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#检查错误","aria-hidden":"true"}},[this._v("#")]),this._v(" 检查错误")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[n("code",[t._v("try_me")]),t._v(" 函数位于项目的根模块。叫做 "),n("code",[t._v("outermost")]),t._v(" 的模块是私有的,不过第二条私有性规则说明 "),n("code",[t._v("try_me")]),t._v(" 函数允许访问 "),n("code",[t._v("outermost")]),t._v(" 模块,因为 "),n("code",[t._v("outermost")]),t._v(" 位于当前(根)模块,"),n("code",[t._v("try_me")]),t._v(" 也是。")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[n("code",[t._v("outermost::middle_function")]),t._v(" 的调用是正确的。因为 "),n("code",[t._v("middle_function")]),t._v(" 是公有的,而 "),n("code",[t._v("try_me")]),t._v(" 通过其父模块 "),n("code",[t._v("outermost")]),t._v(" 访问 "),n("code",[t._v("middle_function")]),t._v("。根据上一段的规则我们可以确定这个模块是可访问的。")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[n("code",[t._v("outermost::middle_secret_function")]),t._v(" 的调用会造成一个编译错误。"),n("code",[t._v("middle_secret_function")]),t._v(" 是私有的,所以第二条(私有性)规则生效了。根模块既不是 "),n("code",[t._v("middle_secret_function")]),t._v(" 的当前模块("),n("code",[t._v("outermost")]),t._v(" 是),也不是 "),n("code",[t._v("middle_secret_function")]),t._v(" 当前模块的子模块。")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("p",[t._v("叫做 "),n("code",[t._v("inside")]),t._v(" 的模块是私有的且没有子模块,所以它只能被当前模块 "),n("code",[t._v("outermost")]),t._v(" 访问。这意味着 "),n("code",[t._v("try_me")]),t._v(" 函数不允许调用 "),n("code",[t._v("outermost::inside::inner_function")]),t._v(" 或 "),n("code",[t._v("outermost::inside::secret_function")]),t._v(" 中的任何一个。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"修改错误"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#修改错误","aria-hidden":"true"}},[this._v("#")]),this._v(" 修改错误")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("ul",[n("li",[t._v("如果 "),n("code",[t._v("inside")]),t._v(" 模块是公有的?")]),t._v(" "),n("li",[t._v("如果 "),n("code",[t._v("outermost")]),t._v(" 是公有的而 "),n("code",[t._v("inside")]),t._v(" 是私有的?")]),t._v(" "),n("li",[t._v("如果在 "),n("code",[t._v("inner_function")]),t._v(" 函数体中调用 "),n("code",[t._v("::outermost::middle_secret_function()")]),t._v("?(开头的两个冒号意味着从根模块开始引用模块。)")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("接下来,让我们讨论一下使用 "),e("code",[this._v("use")]),this._v(" 关键字将项引入作用域。")])}],!1,null,null,null);r.options.__file="ch07-02-controlling-visibility-with-pub.md";e.default=r.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/41.184751dc.js b/src/.vuepress/dist/assets/js/41.184751dc.js
new file mode 100644
index 0000000..d7a9ca0
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/41.184751dc.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[41],{229:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch07-02-modules-and-use-to-control-scope-and-privacy.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch07-01-mod-and-the-filesystem.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 820ac357f6cf0e866e5a8e7a9c57dd3e17e9f8ca")])]),t._v(" "),n("p",[t._v("Rust 的此部分功能通常被引用为 “模块系统”(“the module system”),不过其包括了一些除模块之外的功能。在本部分我们会讨论:")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),n("p",[t._v("为了将代码组织到模块层次体系中,可以将模块嵌套进其他模块,如示例 7-2 所示:")]),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),n("p",[t._v("示例 7-4 提到了它并不能编译,让我们尝试编译并看看为什么不行!示例 7-5 展示了错误。")]),t._v(" "),t._m(27),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._m(38),t._v(" "),t._m(39),t._v(" "),n("p",[t._v("不过示例 7-6 中的代码仍然产生错误,如示例 7-7 所示:")]),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._v(" "),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),t._m(54),t._v(" "),t._m(55),t._v(" "),t._m(56),t._v(" "),t._m(57),t._v(" "),t._m(58),t._m(59),t._v(" "),t._m(60),t._v(" "),t._m(61),t._v(" "),n("p",[t._v("可以以模块与函数相同的方式来设计公有的结构体和枚举,不过有一些额外的细节。")]),t._v(" "),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._m(65),t._v(" "),t._m(66),t._v(" "),t._m(67),t._v(" "),t._m(68),t._v(" "),t._m(69),t._m(70),t._v(" "),t._m(71),t._v(" "),t._m(72),t._v(" "),t._m(73),t._v(" "),t._m(74),t._v(" "),t._m(75),t._v(" "),t._m(76),t._m(77),t._v(" "),t._m(78),t._v(" "),t._m(79),t._v(" "),t._m(80),t._v(" "),t._m(81),t._m(82),t._v(" "),t._m(83),t._v(" "),t._m(84),t._v(" "),t._m(85),t._v(" "),t._m(86),t._v(" "),t._m(87),t._m(88),t._v(" "),t._m(89),t._v(" "),t._m(90),t._v(" "),t._m(91),t._v(" "),t._m(92),t._m(93),t._v(" "),t._m(94),t._v(" "),t._m(95),t._v(" "),t._m(96),t._m(97),t._v(" "),t._m(98),t._v(" "),t._m(99),t._v(" "),t._m(100),t._m(101),t._v(" "),t._m(102),t._v(" "),t._m(103),t._v(" "),t._m(104),t._v(" "),t._m(105),t._v(" "),t._m(106),t._m(107),t._v(" "),t._m(108),t._v(" "),t._m(109),t._v(" "),t._m(110),t._v(" "),t._m(111),t._v(" "),t._m(112),t._v(" "),t._m(113),t._m(114),t._v(" "),t._m(115),t._v(" "),t._m(116),t._v(" "),t._m(117),t._v(" "),t._m(118),t._v(" "),t._m(119),t._m(120),t._v(" "),t._m(121),t._v(" "),t._m(122),t._m(123),t._v(" "),t._m(124),t._v(" "),t._m(125),t._m(126),t._v(" "),t._m(127),t._v(" "),t._m(128),t._v(" "),t._m(129),t._v(" "),t._m(130),n("p",[t._v("可以使用嵌套的路径将同样的项在一行中引入而不是两行,这么做需要指定路径的相同部分,接着是两个分号,接着是大括号中的各自不同的路径部分,如示例 7-22 所示。")]),t._v(" "),t._m(131),t._v(" "),t._m(132),t._m(133),t._v(" "),t._m(134),t._v(" "),t._m(135),t._v(" "),t._m(136),t._v(" "),t._m(137),t._m(138),t._v(" "),t._m(139),t._v(" "),t._m(140),t._v(" "),t._m(141),t._m(142),t._v(" "),t._m(143),t._v(" "),t._m(144),t._v(" "),t._m(145),t._v(" "),t._m(146),t._m(147),t._v(" "),n("p",[t._v("使用 glob 运算符时请多加小心!如此难以推导作用域中有什么名称和它们是在何处定义的。")]),t._v(" "),n("p",[t._v("glob 运算符经常用于测试模块 "),n("code",[t._v("tests")]),t._v(" 中,这时会将所有内容引入作用域;我们将在第十一章 “如何编写测试” 部分讲解。glob 运算符有时也用于 prelude 模式;查看 "),n("a",{attrs:{href:"https://doc.rust-lang.org/std/prelude/index.html#other-preludes",target:"_blank",rel:"noopener noreferrer"}},[t._v("标准库中的文档"),n("OutboundLink")],1),t._v(" 了解这个模式的更多细节。")]),t._v(" "),t._m(148),t._v(" "),n("p",[t._v("目前本章所有的例子都在一个文件中定义多个模块。当模块变得更大时,你可能想要将它们的定义移动到一个单独的文件中使代码更容易阅读。")]),t._v(" "),t._m(149),t._v(" "),t._m(150),t._v(" "),t._m(151),t._m(152),t._v(" "),t._m(153),t._v(" "),t._m(154),t._v(" "),t._m(155),t._m(156),t._v(" "),t._m(157),t._v(" "),t._m(158),t._v(" "),t._m(159),t._v(" "),t._m(160),t._m(161),t._v(" "),t._m(162),t._v(" "),t._m(163),t._m(164),t._v(" "),t._m(165),t._v(" "),t._m(166),t._v(" "),n("p",[t._v("接下来,让我们看看一些标准库提供的集合数据类型,你可以利用它们编写出漂亮整洁的代码。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"模块系统用来控制作用域和私有性"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#模块系统用来控制作用域和私有性","aria-hidden":"true"}},[this._v("#")]),this._v(" 模块系统用来控制作用域和私有性")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ul",[n("li",[t._v("模块,一个组织代码和控制路径私有性的方式")]),t._v(" "),n("li",[t._v("路径,一个命名项(item)的方式")]),t._v(" "),n("li",[n("code",[t._v("use")]),t._v(" 关键字用来将路径引入作用域")]),t._v(" "),n("li",[n("code",[t._v("pub")]),t._v(" 关键字使项变为公有")]),t._v(" "),n("li",[n("code",[t._v("as")]),t._v(" 关键字用于将项引入作用域时进行重命名")]),t._v(" "),n("li",[t._v("使用外部包")]),t._v(" "),n("li",[t._v("嵌套路径用来消除大量的 "),n("code",[t._v("use")]),t._v(" 语句")]),t._v(" "),n("li",[t._v("使用 glob 运算符将模块的所有内容引入作用域")]),t._v(" "),n("li",[t._v("如何将不同模块分割到单独的文件中")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("首先讲讲模块。模块允许我们将代码组织起来。示例 7-1 是一个例子,这些代码定义了名为 "),s("code",[this._v("sound")]),this._v(" 的模块,其包含名为 "),s("code",[this._v("guitar")]),this._v(" 的函数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" sound "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("guitar")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-1: 包含 "),s("code",[this._v("guitar")]),this._v(" 函数和 "),s("code",[this._v("main")]),this._v(" 函数的 "),s("code",[this._v("sound")]),this._v(" 模块")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里定义了两个函数,"),n("code",[t._v("guitar")]),t._v(" 和 "),n("code",[t._v("main")]),t._v("。"),n("code",[t._v("guitar")]),t._v(" 函数定义于 "),n("code",[t._v("mod")]),t._v(" 块中。这个块定义了 "),n("code",[t._v("sound")]),t._v(" 模块。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" sound "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" instrument "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" woodwind "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" voice "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-2: 模块中的模块")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在这个例子中,我们像示例 7-1 一样定义了 "),n("code",[t._v("sound")]),t._v(" 模块。接着在 "),n("code",[t._v("sound")]),t._v(" 模块中定义了 "),n("code",[t._v("instrument")]),t._v(" 和 "),n("code",[t._v("voice")]),t._v(" 模块。"),n("code",[t._v("instrument")]),t._v(" 模块中定义了另一个模块 "),n("code",[t._v("woodwind")]),t._v(",这个模块包含一个函数 "),n("code",[t._v("clarinet")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 “包和 crate 用来创建库和二进制项目” 部分提到 "),n("em",[t._v("src/main.rs")]),t._v(" 和 "),n("em",[t._v("src/lib.rs")]),t._v(" 被称为 "),n("strong",[t._v("crate 根")]),t._v("。他们被称为 crate 根是因为这两个文件在 crate 模块树的根组成了名为 "),n("code",[t._v("crate")]),t._v(" 模块。所以示例 7-2 中,有如示例 7-3 所示的模块树:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("crate\n └── sound\n └── instrument\n └── woodwind\n └── voice\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-3: 示例 7-2 中代码的模块树")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这个树展示了模块如何嵌套在其他模块中(比如 "),n("code",[t._v("woodwind")]),t._v(" 嵌套在 "),n("code",[t._v("instrument")]),t._v(" 中)以及模块如何作为其他模块的子模块的("),n("code",[t._v("instrument")]),t._v(" 和 "),n("code",[t._v("voice")]),t._v(" 都定义在 "),n("code",[t._v("sound")]),t._v(" 中)。整个模块树都位于名为 "),n("code",[t._v("crate")]),t._v(" 这个隐式模块的根下。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个树可能会令你想起电脑上文件系统的目录树;这是一个非常恰当的比喻!就像文件系统的目录,将代码放入任意模块也将创建对应的组织结构体。另一个相似点是为了引用文件系统或模块树中的项,需要使用 "),s("strong",[this._v("路径")]),this._v("("),s("em",[this._v("path")]),this._v(")。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"路径用来引用模块树中的项"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#路径用来引用模块树中的项","aria-hidden":"true"}},[this._v("#")]),this._v(" 路径用来引用模块树中的项")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果想要调用函数,需要知道其 "),s("strong",[this._v("路径")]),this._v("。“路径” 是 “名称”(“name”) 的同义词,不过它用于文件系统语境。另外,函数、结构体和其他项可能会有多个指向相同项的路径,所以 “名称” 这个概念不太准确。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("路径")]),this._v(" 可以有两种形式:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ul",[n("li",[n("strong",[t._v("绝对路径")]),t._v("("),n("em",[t._v("absolute path")]),t._v(")从 crate 根开始,以 crate 名或者字面值 "),n("code",[t._v("crate")]),t._v(" 开头。")]),t._v(" "),n("li",[n("strong",[t._v("相对路径")]),t._v("("),n("em",[t._v("relative path")]),t._v(")从当前模块开始,以 "),n("code",[t._v("self")]),t._v("、"),n("code",[t._v("super")]),t._v(" 或当前模块的标识符开头。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("绝对路径和相对路径都后跟一个或多个由双分号("),s("code",[this._v("::")]),this._v(")分割的标识符。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如何在示例 7-2 的 "),n("code",[t._v("main")]),t._v(" 函数中调用 "),n("code",[t._v("clarinet")]),t._v(" 函数呢?也就是说,"),n("code",[t._v("clarinet")]),t._v(" 函数的路径是什么呢?在示例 7-4 中稍微简化了代码,移除了一些模块,并展示了两种在 "),n("code",[t._v("main")]),t._v(" 中调用 "),n("code",[t._v("clarinet")]),t._v(" 函数的方式。这个例子还不能编译,我们会解释为什么。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("mod sound {\n mod instrument {\n fn clarinet() {\n // 函数体\n }\n }\n}\n\nfn main() {\n // 绝对路径\n crate::sound::instrument::clarinet();\n\n // Relative path\n sound::instrument::clarinet();\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-4: 在简化的模块树中,分别使用绝对路径和相对路径在 "),s("code",[this._v("main")]),this._v(" 中调用 "),s("code",[this._v("clarinet")]),this._v(" 函数")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("第一种从 "),n("code",[t._v("main")]),t._v(" 函数中调用 "),n("code",[t._v("clarinet")]),t._v(" 函数的方式使用绝对路径。因为 "),n("code",[t._v("clarinet")]),t._v(" 与 "),n("code",[t._v("main")]),t._v(" 定义于同一 crate 中,我们使用 "),n("code",[t._v("crate")]),t._v(" 关键字来开始绝对路径。接着包含每一个模块直到 "),n("code",[t._v("clarinet")]),t._v("。这类似于指定 "),n("code",[t._v("/sound/instrument/clarinet")]),t._v(" 来运行电脑上这个位置的程序;使用 "),n("code",[t._v("crate")]),t._v(" 从 crate 根开始就类似于在 shell 中使用 "),n("code",[t._v("/")]),t._v(" 从文件系统根开始。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("第二种从 "),n("code",[t._v("main")]),t._v(" 函数中调用 "),n("code",[t._v("clarinet")]),t._v(" 函数的方式使用相对路径。该路径以 "),n("code",[t._v("sound")]),t._v(" 开始,它是定义于与 "),n("code",[t._v("main")]),t._v(" 函数相同模块树级别的模块。这类似于指定 "),n("code",[t._v("sound/instrument/clarinet")]),t._v(" 来运行电脑上这个位置的程序;以名称开头意味着路径是相对的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo build\n Compiling sampleproject v0.1.0 (file:///projects/sampleproject)\nerror[E0603]: module `instrument` is private\n --\x3e src/main.rs:11:19\n |\n11 | crate::sound::instrument::clarinet();\n | ^^^^^^^^^^\n\nerror[E0603]: module `instrument` is private\n --\x3e src/main.rs:14:12\n |\n14 | sound::instrument::clarinet();\n | ^^^^^^^^^^\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-5: 构建示例 7-4 出现的编译器错误")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("错误信息说 "),n("code",[t._v("instrument")]),t._v(" 模块是私有的。可以看到 "),n("code",[t._v("instrument")]),t._v(" 模块和 "),n("code",[t._v("clarinet")]),t._v(" 函数的路径是正确的,不过 Rust 不让我们使用,因为他们是私有的。现在是学习 "),n("code",[t._v("pub")]),t._v(" 关键字的时候了!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"模块作为私有性的边界"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#模块作为私有性的边界","aria-hidden":"true"}},[this._v("#")]),this._v(" 模块作为私有性的边界")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("之前我们讨论到模块的语法和组织代码的用途。Rust 采用模块还有另一个原因:模块是 Rust 中的 "),s("strong",[this._v("私有性边界")]),this._v("("),s("em",[this._v("privacy boundary")]),this._v(")。如果你希望函数或结构体是私有的,将其放入模块。私有性规则有如下:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ul",[n("li",[t._v("所有项(函数、方法、结构体、枚举、模块和常量)默认是私有的。")]),t._v(" "),n("li",[t._v("可以使用 "),n("code",[t._v("pub")]),t._v(" 关键字使项变为共有。")]),t._v(" "),n("li",[t._v("不允许使用定义于当前模块的子模块中的私有代码。")]),t._v(" "),n("li",[t._v("允许使用任何定义于父模块或当前模块中的代码。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("换句话说,对于没有 "),s("code",[this._v("pub")]),this._v(" 关键字的项,当你从当前模块向 “下” 看时是私有的,不过当你向 “上” 看时是公有的。再一次想象一下文件系统:如果你没有某个目录的权限,则无法从父目录中查看其内容。如果有该目录的权限,则可以查看其中的目录和任何父目录。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-pub-关键字使项变为公有"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-pub-关键字使项变为公有","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("pub")]),this._v(" 关键字使项变为公有")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 7-5 中的错误说 "),n("code",[t._v("instrument")]),t._v(" 模块使私有的。让我们使用 "),n("code",[t._v("pub")]),t._v(" 关键字标记 "),n("code",[t._v("instrument")]),t._v(" 模块使其可以在 "),n("code",[t._v("main")]),t._v(" 函数中使用。这些改变如示例 7-6 所示,它仍然不能编译,不过会产生一个不同的错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("mod sound {\n pub mod instrument {\n fn clarinet() {\n // 函数体\n }\n }\n}\n\nfn main() {\n // Absolute path\n crate::sound::instrument::clarinet();\n\n // Relative path\n sound::instrument::clarinet();\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-6: 将 "),s("code",[this._v("instrument")]),this._v(" 模块声明为 "),s("code",[this._v("pub")]),this._v(" 以便可以在 "),s("code",[this._v("main")]),this._v(" 中使用")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 "),n("code",[t._v("mod instrument")]),t._v(" 之前增加 "),n("code",[t._v("pub")]),t._v(" 关键字使得模块变为公有。通过这个改变如果允许访问 "),n("code",[t._v("sound")]),t._v(" 的话,我们就可以访问 "),n("code",[t._v("instrument")]),t._v("。"),n("code",[t._v("instrument")]),t._v(" 的内容仍然是私有的;使得模块公有并不使其内容也是公有的。模块上的 "),n("code",[t._v("pub")]),t._v(" 关键字允许其父模块引用它。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo build\n Compiling sampleproject v0.1.0 (file:///projects/sampleproject)\nerror[E0603]: function `clarinet` is private\n --\x3e src/main.rs:11:31\n |\n11 | crate::sound::instrument::clarinet();\n | ^^^^^^^^\n\nerror[E0603]: function `clarinet` is private\n --\x3e src/main.rs:14:24\n |\n14 | sound::instrument::clarinet();\n | ^^^^^^^^\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-7: 构建示例 7-6 时产生的编译器错误")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在的错误表明 "),s("code",[this._v("clarinet")]),this._v(" 函数是私有的。私有性规则适用于结构体、枚举、函数和方法以及模块。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 "),s("code",[this._v("clarinet")]),this._v(" 函数前增加 "),s("code",[this._v("pub")]),this._v(" 关键字使其变为公有,如示例 7-8 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" sound "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" instrument "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 绝对路径")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("crate")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sound"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 相对路径")]),t._v("\n sound"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("span",{staticClass:"caption"},[t._v("示例 7-8: 在 "),n("code",[t._v("mod instrument")]),t._v(" 和 "),n("code",[t._v("fn clarinet")]),t._v(" 之前都增加 "),n("code",[t._v("pub")]),t._v(" 关键字使我们可以在 "),n("code",[t._v("main")]),t._v(" 中调用此函数")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在可以编译了!让我们看看绝对路径和相对路径再次检查为什么增加 "),s("code",[this._v("pub")]),this._v(" 关键字使得我们可以在 "),s("code",[this._v("main")]),this._v(" 中调用这些路径。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在绝对路径的情况下,我们从 "),n("code",[t._v("crate")]),t._v(",也就是 crate 根开始。从这开始有 "),n("code",[t._v("sound")]),t._v(",这是一个定义于 crate 根中的模块。"),n("code",[t._v("sound")]),t._v(" 模块不是公有的,不过因为 "),n("code",[t._v("main")]),t._v(" 函数与 "),n("code",[t._v("sound")]),t._v(" 定义于同一模块中,可以从 "),n("code",[t._v("main")]),t._v(" 中引用 "),n("code",[t._v("sound")]),t._v("。接下来是 "),n("code",[t._v("instrument")]),t._v(",这个模块标记为 "),n("code",[t._v("pub")]),t._v("。我们可以访问 "),n("code",[t._v("instrument")]),t._v(" 的父模块,所以可以访问 "),n("code",[t._v("instrument")]),t._v("。最后,"),n("code",[t._v("clarinet")]),t._v(" 函数被标记为 "),n("code",[t._v("pub")]),t._v(" 所以可以访问其父模块,所以这个函数调用是有效的!")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在相对路径的情况下,其逻辑与绝对路径相同,除了第一步。不同于从 crate 根开始,路径从 "),n("code",[t._v("sound")]),t._v(" 开始。"),n("code",[t._v("sound")]),t._v(" 模块与 "),n("code",[t._v("main")]),t._v(" 定义于同一模块,所以从 "),n("code",[t._v("main")]),t._v(" 所在模块开始定义的路径是有效的。接下来因为 "),n("code",[t._v("instrument")]),t._v(" 和 "),n("code",[t._v("clarinet")]),t._v(" 被标记为 "),n("code",[t._v("pub")]),t._v(",路径其余的部分也是有效的,因此函数调用也是有效的!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-super-开始相对路径"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-super-开始相对路径","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("super")]),this._v(" 开始相对路径")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("也可以使用 "),n("code",[t._v("super")]),t._v(" 开头来构建相对路径。这么做类似于文件系统中以 "),n("code",[t._v("..")]),t._v(" 开头:该路径从 "),n("strong",[t._v("父")]),t._v(" 模块开始而不是当前模块。这在例如示例 7-9 这样的情况下有用处,在这里 "),n("code",[t._v("clarinet")]),t._v(" 函数通过指定以 "),n("code",[t._v("super")]),t._v(" 开头的路径调用 "),n("code",[t._v("breathe_in")]),t._v(" 函数:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" instrument "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("super")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("breathe_in")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("breathe_in")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-9: 使用以 "),s("code",[this._v("super")]),this._v(" 开头的路径从父目录开始调用函数")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("clarinet")]),t._v(" 函数位于 "),n("code",[t._v("instrument")]),t._v(" 模块中,所以可以使用 "),n("code",[t._v("super")]),t._v(" 进入 "),n("code",[t._v("instrument")]),t._v(" 的父模块,也就是根 "),n("code",[t._v("crate")]),t._v("。从这里可以找到 "),n("code",[t._v("breathe_in")]),t._v("。成功!")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("你可能想要使用 "),n("code",[t._v("super")]),t._v(" 开头的相对路而不是以 "),n("code",[t._v("crate")]),t._v(" 开头的绝对路径的原因是 "),n("code",[t._v("super")]),t._v(" 可能会使修改有着不同模块层级结构的代码变得更容易,如果定义项和调用项的代码被一同移动的话。例如,如果我们决定将 "),n("code",[t._v("instrument")]),t._v(" 模块和 "),n("code",[t._v("breathe_in")]),t._v(" 函数放入 "),n("code",[t._v("sound")]),t._v(" 模块中,这时我们只需增加 "),n("code",[t._v("sound")]),t._v(" 模块即可,如示例 7-10 所示。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" sound "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" instrument "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("super")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("breathe_in")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("breathe_in")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// Function body code goes here")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-10: 增加一个名为 "),s("code",[this._v("sound")]),this._v(" 的父模块并不影响相对路径 "),s("code",[this._v("super::breathe_in")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 7-10 在 "),n("code",[t._v("clarinet")]),t._v(" 函数中调用 "),n("code",[t._v("super::breathe_in")]),t._v(" 将如示例 7-9 一样继续有效,无需更新路径。如果在 "),n("code",[t._v("clarinet")]),t._v(" 函数不使用 "),n("code",[t._v("super::breathe_in")]),t._v(" 而是使用 "),n("code",[t._v("crate::breathe_in")]),t._v(" 的话,当增加父模块 "),n("code",[t._v("sound")]),t._v(" 后,则需要更新 "),n("code",[t._v("clarinet")]),t._v(" 函数使用 "),n("code",[t._v("crate::sound::breathe_in")]),t._v(" 路径。使用相对路径可能意味着重新布局模块时需要更少的必要修改。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"对结构体和枚举使用-pub"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#对结构体和枚举使用-pub","aria-hidden":"true"}},[this._v("#")]),this._v(" 对结构体和枚举使用 "),s("code",[this._v("pub")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如果在结构体定义中使用 "),n("code",[t._v("pub")]),t._v(",可以使结构体公有。然而结构体的字段仍是私有的。可以在每一个字段的基准上选择其是否公有。在示例 7-11 中定义了一个公有结构体 "),n("code",[t._v("plant::Vegetable")]),t._v(",其包含公有的 "),n("code",[t._v("name")]),t._v(" 字段和私有的 "),n("code",[t._v("id")]),t._v(" 字段。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" plant "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Vegetable "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" name"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Vegetable "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Vegetable "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Vegetable "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n id"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" v "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" plant"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Vegetable"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"squash"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n v"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"butternut squash"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"{} are delicious"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 如果将如下行取消注释代码将无法编译:")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v('// println!("The ID is {}", v.id);')]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-11: 结构体带有公有和私有的字段")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("因为 "),n("code",[t._v("plant::Vegetable")]),t._v(" 结构体的 "),n("code",[t._v("name")]),t._v(" 字段使公有的,在 "),n("code",[t._v("main")]),t._v(" 中可以使用点号读写 "),n("code",[t._v("name")]),t._v(" 字段。不允许在 "),n("code",[t._v("main")]),t._v(" 中使用 "),n("code",[t._v("id")]),t._v(" 字段因为其使私有的。尝试取消注释的行来打印 "),n("code",[t._v("id")]),t._v(" 字段的值来看看会出现什么错误!另外注意因为 "),n("code",[t._v("plant::Vegetable")]),t._v(" 有私有字段,需要提供一个公有的关联函数来构建 "),n("code",[t._v("Vegetable")]),t._v(" 的实例(这里使用了传统的名称 "),n("code",[t._v("new")]),t._v(")。如果 "),n("code",[t._v("Vegetable")]),t._v(" 没有提供这么一个函数,我们就不能在 "),n("code",[t._v("main")]),t._v(" 中创建 "),n("code",[t._v("Vegetable")]),t._v(" 的实例,因为在 "),n("code",[t._v("main")]),t._v(" 中不允许设置私有字段 "),n("code",[t._v("id")]),t._v(" 的值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("相反,如果有一个公有枚举,其所有成员都是公有。只需在 "),s("code",[this._v("enum")]),this._v(" 关键词前加上 "),s("code",[this._v("pub")]),this._v(",如示例 7-12 所示。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" menu "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Appetizer "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Soup"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Salad"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" order1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" menu"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Appetizer"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Soup"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" order2 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" menu"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Appetizer"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Salad"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-12: 将枚举设计为公有会使其所有成员公有")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("因为 "),n("code",[t._v("Appetizer")]),t._v(" 枚举是公有的,可以在 "),n("code",[t._v("main")]),t._v(" 中使用 "),n("code",[t._v("Soup")]),t._v(" and "),n("code",[t._v("Salad")]),t._v(" 成员。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("还有一种使用 "),n("code",[t._v("pub")]),t._v(" 的场景我们还没有涉及到,而这是我们最后要讲的模块功能:"),n("code",[t._v("use")]),t._v(" 关键字。我们先单独介绍 "),n("code",[t._v("use")]),t._v(",然后展示如何结合使用 "),n("code",[t._v("pub")]),t._v(" 和 "),n("code",[t._v("use")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-use-关键字将名称引入作用域"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-use-关键字将名称引入作用域","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("use")]),this._v(" 关键字将名称引入作用域")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("你可能考虑过本章很多的函数调用的路径是冗长和重复的。例如示例 7-8 中,当我们选择 "),n("code",[t._v("clarinet")]),t._v(" 函数的绝对或相对路径时,每次想要调用 "),n("code",[t._v("clarinet")]),t._v(" 时都不得不也指定 "),n("code",[t._v("sound")]),t._v(" 和 "),n("code",[t._v("instrument")]),t._v("。幸运的是,有一次性将路径引入作用域然后就像调用本地项那样的方法:使用 "),n("code",[t._v("use")]),t._v(" 关键字。在示例 7-13 中将 "),n("code",[t._v("crate::sound::instrument")]),t._v(" 模块引入了 "),n("code",[t._v("main")]),t._v(" 函数的作用域,以便只需指定 "),n("code",[t._v("instrument::clarinet")]),t._v(" 来调用 "),n("code",[t._v("clarinet")]),t._v(" 函数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" sound "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" instrument "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("crate")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sound"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-13: 使用 "),s("code",[this._v("use")]),this._v(" 将模块引入作用域并使用绝对路径来缩短在模块中调用项所必须的路径")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当指定 "),s("code",[this._v("use")]),this._v(" 后以 "),s("code",[this._v("self")]),this._v(" 开头的相对路径在未来可能不是必须的;这是一个开发者正在尽力消除的语言中的不一致。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如果调用项目的代码移动到模块树的不同位置但是定义项目的代码却没有,那么选择使用 "),n("code",[t._v("use")]),t._v(" 指定绝对路径可以使更新更轻松,这与示例 7-10 中同时移动的情况相对。例如,如果我们决定采用示例 7-13 的代码,将 "),n("code",[t._v("main")]),t._v(" 函数的行为提取到函数 "),n("code",[t._v("clarinet_trio")]),t._v(" 中,并将该函数移动到模块 "),n("code",[t._v("performance_group")]),t._v(" 中,这时 "),n("code",[t._v("use")]),t._v(" 所指定的路径无需变化,如示例 7-15 所示。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" sound "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" instrument "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" performance_group "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("crate")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sound"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet_trio")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n performance_group"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet_trio")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-15: 移动调用项的代码时绝对路径无需移动")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("相反,如果对示例 7-14 中指定了相对路径的代码做同样的修改,则需要将 "),s("code",[this._v("use self::sound::instrument")]),this._v(" 变为 "),s("code",[this._v("use super::sound::instrument")]),this._v("。如果你不确定将来模块树会如何变化,那么选择采用相对或绝对路径是否会减少修改可能全靠猜测,不过本书的作者倾向于通过 "),s("code",[this._v("crate")]),this._v(" 指定绝对路径,因为定义和调用项的代码更有可能相互独立的在模块树中移动,而不是像示例 7-10 那样一同移动。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"use-函数路径使用习惯-vs-其他项"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#use-函数路径使用习惯-vs-其他项","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("use")]),this._v(" 函数路径使用习惯 VS 其他项")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 7-13 中,你可能会好奇为什么指定 "),s("code",[this._v("use crate::sound::instrument")]),this._v(" 接着在 "),s("code",[this._v("main")]),this._v(" 中调用 "),s("code",[this._v("instrument::clarinet")]),this._v(",而不是如示例 7-16 所示的有相同行为的代码:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" sound "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" instrument "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("crate")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sound"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("clarinet"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-16: 通过 "),s("code",[this._v("use")]),this._v(" 将 "),s("code",[this._v("clarinet")]),this._v(" 函数引入作用域,这是不推荐的")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("对于函数来说,通过 "),s("code",[this._v("use")]),this._v(" 指定函数的父模块接着指定父模块来调用方法被认为是习惯用法。这么做而不是像示例 7-16 那样通过 "),s("code",[this._v("use")]),this._v(" 指定函数的路径,清楚的表明了函数不是本地定义的,同时仍最小化了指定全路径时的重复。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("对于结构体、枚举和其它项,通过 "),s("code",[this._v("use")]),this._v(" 指定项的全路径是习惯用法。例如,示例 7-17 展示了将标准库中 "),s("code",[this._v("HashMap")]),this._v(" 结构体引入作用域的习惯用法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" map "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" HashMap"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n map"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("insert")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-17: 将 "),s("code",[this._v("HashMap")]),this._v(" 引入作用域的习惯用法")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("相反,示例 7-18 中的代码将 "),s("code",[this._v("HashMap")]),this._v(" 的父模块引入作用域不被认为是习惯用法。这个习惯并没有很强制的理由;这是慢慢形成的习惯同时人们习惯于这么读写。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" map "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" collections"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n map"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("insert")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-18: 将 "),s("code",[this._v("HashMap")]),this._v(" 引入作用域的非习惯方法")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个习惯的一个例外是如果 "),s("code",[this._v("use")]),this._v(" 语句会将两个同名的项引入作用域时,这是不允许的。示例 7-19 展示了如何将两个不同父模块的 "),s("code",[this._v("Result")]),this._v(" 类型引入作用域并引用它们。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fmt"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("function1")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" fmt"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Result "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("function2")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-19: 将两个同名类型引入作用域必须使用父模块")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("因为如果我们指定 "),n("code",[t._v("use std::fmt::Result")]),t._v(" 和 "),n("code",[t._v("use std::io::Result")]),t._v(",则作用域中会有两个 "),n("code",[t._v("Result")]),t._v(" 类型,Rust 无法知道我们想用哪个 "),n("code",[t._v("Result")]),t._v("。尝试这么做并看看编译器错误!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"通过-as-关键字重命名引入作用域的类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#通过-as-关键字重命名引入作用域的类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 通过 "),s("code",[this._v("as")]),this._v(" 关键字重命名引入作用域的类型")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("将两个同名类型引入同一作用域这个问题还有另一个解决办法:可以通过在 "),n("code",[t._v("use")]),t._v(" 后加上 "),n("code",[t._v("as")]),t._v(" 和一个新名称来为此类型指定一个新的本地名称。示例 7-20 展示了另一个编写示例 7-19 中代码的方法,通过 "),n("code",[t._v("as")]),t._v(" 重命名了其中一个 "),n("code",[t._v("Result")]),t._v(" 类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fmt"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Result"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Result "),n("span",{attrs:{class:"token keyword"}},[t._v("as")]),t._v(" IoResult"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("function1")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("function2")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" IoResult"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-20: 通过 "),s("code",[this._v("as")]),this._v(" 关键字重命名引入作用域的类型")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在第二个 "),n("code",[t._v("use")]),t._v(" 语句中,我们选择 "),n("code",[t._v("IoResult")]),t._v(" 作为 "),n("code",[t._v("std::io::Result")]),t._v(" 的新名称,它与从 "),n("code",[t._v("std::fmt")]),t._v(" 引入作用域的 "),n("code",[t._v("Result")]),t._v(" 并不冲突。这样做也被认为是惯用的;示例 7-19 还是示例 7-20 全看你的选择。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"通过-pub-use-重导出名称"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#通过-pub-use-重导出名称","aria-hidden":"true"}},[this._v("#")]),this._v(" 通过 "),s("code",[this._v("pub use")]),this._v(" 重导出名称")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当使用 "),n("code",[t._v("use")]),t._v(" 关键字将名称导入作用域时,在新作用域中可用的名称是私有的。如果希望调用你编写的代码的代码能够像你一样在其自己的作用域内引用这些类型,可以结合 "),n("code",[t._v("pub")]),t._v(" 和 "),n("code",[t._v("use")]),t._v("。这个技术被称为 “重导出”("),n("em",[t._v("re-exporting")]),t._v("),因为这样做将项引入作用域并同时使其可供其他代码引入自己的作用域。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("例如,示例 7-21 展示了示例 7-15 中的代码将 "),s("code",[this._v("performance_group")]),this._v(" 的 "),s("code",[this._v("use")]),this._v(" 变为 "),s("code",[this._v("pub use")]),this._v(" 的版本。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" sound "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" instrument "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" performance_group "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("crate")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("sound"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet_trio")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n performance_group"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet_trio")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n performance_group"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("instrument"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-21: 通过 "),s("code",[this._v("pub use")]),this._v(" 使名称可引入任何代码的作用域中")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("通过 "),n("code",[t._v("pub use")]),t._v(",现在 "),n("code",[t._v("main")]),t._v(" 函数可以通过新路径 "),n("code",[t._v("performance_group::instrument::clarinet")]),t._v(" 来调用 "),n("code",[t._v("clarinet")]),t._v(" 函数。如果没有指定 "),n("code",[t._v("pub use")]),t._v(","),n("code",[t._v("clarinet_trio")]),t._v(" 函数可以在其作用域中调用 "),n("code",[t._v("instrument::clarinet")]),t._v(" 但 "),n("code",[t._v("main")]),t._v(" 则不允许使用这个新路径。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用外部包"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用外部包","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用外部包")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在第二章中我们编写了一个猜猜看游戏。那个项目使用了一个外部包,"),s("code",[this._v("rand")]),this._v(",来生成随机数。为了在项目中使用 "),s("code",[this._v("rand")]),this._v(",在 "),s("em",[this._v("Cargo.toml")]),this._v(" 中加入了如下行:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-toml extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('[dependencies]\nrand = "0.5.5"\n')])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 "),n("em",[t._v("Cargo.toml")]),t._v(" 中加入 "),n("code",[t._v("rand")]),t._v(" 依赖告诉了 Cargo 要从 "),n("em",[t._v("https://crates.io")]),t._v(" 下载 "),n("code",[t._v("rand")]),t._v(" 和其依赖,并使其可在项目代码中使用。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("接着,为了将 "),n("code",[t._v("rand")]),t._v(" 定义引入项目包的作用域,加入一行 "),n("code",[t._v("use")]),t._v(",它以 "),n("code",[t._v("rand")]),t._v(" 包名开头并列出了需要引入作用域的项。回忆一下第二章的 “生成一个随机数” 部分,我们曾将 "),n("code",[t._v("Rng")]),t._v(" trait 引入作用域并调用了 "),n("code",[t._v("rand::thread_rng")]),t._v(" 函数:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("use rand::Rng;\n\nfn main() {\n let secret_number = rand::thread_rng().gen_range(1, 101);\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("em",[this._v("https://crates.io")]),this._v(" 上有很多社区成员发布的包,将其引入你自己的项目涉及到相同的步骤:在 "),s("em",[this._v("Cargo.toml")]),this._v(" 列出它们并通过 "),s("code",[this._v("use")]),this._v(" 将其中定义的项引入项目包的作用域中。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意标准库("),n("code",[t._v("std")]),t._v(")对于你的包来说也是外部 crate。因为标准库随 Rust 语言一同分发,无需修改 "),n("em",[t._v("Cargo.toml")]),t._v(" 来引入 "),n("code",[t._v("std")]),t._v(",不过需要通过 "),n("code",[t._v("use")]),t._v(" 将标准库中定义的项引入项目包的作用域中来引用它们,比如 "),n("code",[t._v("HashMap")]),t._v(":")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这是一个以标注库 crate 名 "),s("code",[this._v("std")]),this._v(" 开头的绝对路径。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"嵌套路径来消除大量的-use-行"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#嵌套路径来消除大量的-use-行","aria-hidden":"true"}},[this._v("#")]),this._v(" 嵌套路径来消除大量的 "),s("code",[this._v("use")]),this._v(" 行")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当需要引入很多定义于相同包或相同模块的项时,为每一项单独列出一行会占用源码很大的空间。例如猜猜看章节示例 2-4 中有两行 "),s("code",[this._v("use")]),this._v(" 语句都从 "),s("code",[this._v("std")]),this._v(" 引入项到作用域:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("cmp"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Ordering"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token comment"}},[t._v("// ---snip---")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("cmp"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Ordering"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" io"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token comment"}},[t._v("// ---snip---")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-22: 指定嵌套的路径在一行中将多个带有相同前缀的项引入作用域")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在从相同包或模块中引入很多项的程序中,使用嵌套路径显著减少所需的单独 "),s("code",[this._v("use")]),this._v(" 语句!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("也可以剔除掉完全包含在另一个路径中的路径。例如,示例 7-23 中展示了两个 "),s("code",[this._v("use")]),this._v(" 语句:一个将 "),s("code",[this._v("std::io")]),this._v(" 引入作用域,另一个将 "),s("code",[this._v("std::io::Write")]),this._v(" 引入作用域:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Write"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-23: 通过两行 "),s("code",[this._v("use")]),this._v(" 语句引入两个路径,其中一个是另一个的子路径")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("两个路径的相同部分是 "),s("code",[this._v("std::io")]),this._v(",这正是第一个路径。为了在一行 "),s("code",[this._v("use")]),this._v(" 语句中引入这两个路径,可以在嵌套路径中使用 "),s("code",[this._v("self")]),this._v(",如示例 7-24 所示。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Write"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-24: 将示例 7-23 中部分重复的路径合并为一个 "),s("code",[this._v("use")]),this._v(" 语句")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这将 "),s("code",[this._v("std::io")]),this._v(" 和 "),s("code",[this._v("std::io::Write")]),this._v(" 同时引入作用域。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"通过-glob-运算符将所有的公有定义引入作用域"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#通过-glob-运算符将所有的公有定义引入作用域","aria-hidden":"true"}},[this._v("#")]),this._v(" 通过 glob 运算符将所有的公有定义引入作用域")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果希望将一个路径下 "),s("strong",[this._v("所有")]),this._v(" 公有项引入作用域,可以指定路径后跟 "),s("code",[this._v("*")]),this._v(",glob 运算符:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个 "),s("code",[this._v("use")]),this._v(" 语句将 "),s("code",[this._v("std::collections")]),this._v(" 中定义的所有公有项引入当前作用域。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"将模块分割进不同文件"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#将模块分割进不同文件","aria-hidden":"true"}},[this._v("#")]),this._v(" 将模块分割进不同文件")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("例如从示例 7-8 的代码开始,我们可以通过修改 crate 根文件(这里是 "),s("em",[this._v("src/main.rs")]),this._v(")将 "),s("code",[this._v("sound")]),this._v(" 模块移动到其自己的文件 "),s("em",[this._v("src/sound.rs")]),this._v(" 中,如示例 7-25 所示。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("mod sound;\n\nfn main() {\n // 绝对路径\n crate::sound::instrument::clarinet();\n\n // 相对路径\n sound::instrument::clarinet();\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-25: 声明 "),s("code",[this._v("sound")]),this._v(" 模块,其内容位于 "),s("em",[this._v("src/sound.rs")]),this._v(" 文件")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("而 "),s("em",[this._v("src/sound.rs")]),this._v(" 中会包含 "),s("code",[this._v("sound")]),this._v(" 模块的内容,如示例 7-26 所示。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/sound.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" instrument "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-26: "),s("code",[this._v("sound")]),this._v(" 模块中的定义位于 "),s("em",[this._v("src/sound.rs")]),this._v(" 中")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 "),s("code",[this._v("mod sound")]),this._v(" 后使用分号而不是代码块告诉 Rust 在另一个与模块同名的文件中加载模块的内容。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("继续重构我们例子,将 "),s("code",[this._v("instrument")]),this._v(" 模块也提取到其自己的文件中,修改 "),s("em",[this._v("src/sound.rs")]),this._v(" 只包含 "),s("code",[this._v("instrument")]),this._v(" 模块的声明:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/sound.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[this._v("pub")]),this._v(" "),s("span",{attrs:{class:"token keyword"}},[this._v("mod")]),this._v(" instrument"),s("span",{attrs:{class:"token punctuation"}},[this._v(";")]),this._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接着创建 "),s("em",[this._v("src/sound")]),this._v(" 目录和 "),s("em",[this._v("src/sound/instrument.rs")]),this._v(" 文件来包含 "),s("code",[this._v("instrument")]),this._v(" 模块的定义:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/sound/instrument.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("clarinet")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("模块树依然保持相同,"),s("code",[this._v("main")]),this._v(" 中的函数调用也无需修改继续保持有效,即使其定义存在于不同的文件中。这样随着代码增长可以将模块移动到新文件中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 提供了将包组织进 crate、将 crate 组织进模块和通过指定绝对或相对路径从一个模块引用另一个模块中定义的项的方式。可以通过 "),s("code",[this._v("use")]),this._v(" 语句将路径引入作用域,这样在多次使用时可以使用更短的路径。模块定义的代码默认时私有的,不过可以选择增加 "),s("code",[this._v("pub")]),this._v(" 关键字使其定义变为公有。")])}],!1,null,null,null);e.options.__file="ch07-02-modules-and-use-to-control-scope-and-privacy.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/42.9fa6eee5.js b/src/.vuepress/dist/assets/js/42.9fa6eee5.js
new file mode 100644
index 0000000..6e2b4e0
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/42.9fa6eee5.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[42],{228:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch07-03-importing-names-with-use.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch07-03-importing-names-with-use.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 550c8ea6f74060ff1f7b67e7e1878c4da121682d")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),n("p",[t._v("如你所见,指定函数的完全限定名称可能会非常冗长。所幸 Rust 有一个关键字使得这些调用显得更简洁。")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),n("p",[t._v("这使得我们可以忽略所有的模块并直接引用函数。")]),t._v(" "),t._m(13),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._m(34),t._v(" "),t._m(35),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._m(43),t._v(" "),n("p",[t._v("现在你掌握了组织代码的核心科技!利用它们将相关的代码组合在一起、防止代码文件过长并将一个整洁的公有 API 展现给库的用户。")]),t._v(" "),n("p",[t._v("接下来,让我们看看一些标准库提供的集合数据类型,你可以利用它们编写出漂亮整洁的代码。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"引用不同模块中的名称"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#引用不同模块中的名称","aria-hidden":"true"}},[this._v("#")]),this._v(" 引用不同模块中的名称")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们已经讲到了如何使用模块名称作为调用的一部分,来调用模块中的函数,如示例 7-7 中所示的 "),s("code",[this._v("nested_modules")]),this._v(" 函数调用。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" a "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" series "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" of "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("nested_modules")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("series"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("of"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("nested_modules")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 7-7:通过完全指定模块中的路径来调用函数")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-use-关键字将名称导入作用域"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-use-关键字将名称导入作用域","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("use")]),this._v(" 关键字将名称导入作用域")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 的 "),s("code",[this._v("use")]),this._v(" 关键字能将你想要调用的函数所在的模块引入到当前作用域中,通过这种方式可以缩短冗长的函数调用。这是一个将 "),s("code",[this._v("a::series::of")]),this._v(" 模块导入一个二进制 crate 的根作用域的例子:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" a "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" series "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" of "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("nested_modules")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" a"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("series"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("of"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n of"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("nested_modules")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("use a::series::of;")]),t._v(" 这一行的意思是每当想要引用 "),n("code",[t._v("of")]),t._v(" 模块时,不必使用完整的 "),n("code",[t._v("a::series::of")]),t._v(" 路径,可以直接使用 "),n("code",[t._v("of")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("use")]),this._v(" 关键字只将指定的模块引入作用域;它并不会将其子模块也引入。这就是为什么想要调用 "),s("code",[this._v("nested_modules")]),this._v(" 函数时仍然必须写成 "),s("code",[this._v("of::nested_modules")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("也可以将函数本身引入到作用域中,通过如下在 "),s("code",[this._v("use")]),this._v(" 中指定函数的方式:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" a "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" series "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" of "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("nested_modules")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" a"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("series"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("of"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("nested_modules"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("nested_modules")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为枚举也像模块一样组成了某种命名空间,也可以使用 "),s("code",[this._v("use")]),this._v(" 来导入枚举的成员。对于任何类型的 "),s("code",[this._v("use")]),this._v(" 语句,如果从一个命名空间导入多个项,可以在最后使用大括号和逗号来列举它们,像这样:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" TrafficLight "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Red"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Yellow"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Green"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" TrafficLight"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("Red"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Yellow"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" red "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Red"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" yellow "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Yellow"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" green "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" TrafficLight"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Green"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们仍然为 "),n("code",[t._v("Green")]),t._v(" 成员指定了 "),n("code",[t._v("TrafficLight")]),t._v(" 命名空间,因为并没有在 "),n("code",[t._v("use")]),t._v(" 语句中包含 "),n("code",[t._v("Green")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-glob-将所有名称引入作用域"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-glob-将所有名称引入作用域","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 glob 将所有名称引入作用域")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为了一次将某个命名空间下的所有名称都引入作用域,可以使用 "),s("code",[this._v("*")]),this._v(" 语法,这称为 "),s("strong",[this._v("glob 运算符")]),this._v("("),s("em",[this._v("glob operator")]),this._v(")。这个例子将一个枚举的所有成员引入作用域而没有将其一一列举出来:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" TrafficLight "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Red"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Yellow"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Green"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" TrafficLight"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" red "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Red"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" yellow "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Yellow"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" green "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Green"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("*")]),this._v(" 会将 "),s("code",[this._v("TrafficLight")]),this._v(" 命名空间中所有可见的项都引入作用域。请保守的使用 glob:它们是方便的,但是也可能会引入多于预期的内容从而导致命名冲突。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-super-访问父模块"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-super-访问父模块","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("super")]),this._v(" 访问父模块")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("正如我们已经知道的,当创建一个库 crate 时,Cargo 会生成一个 "),s("code",[this._v("tests")]),this._v(" 模块。现在让我们来深入了解一下。在 "),s("code",[this._v("communicator")]),this._v(" 项目中,打开 "),s("em",[this._v("src/lib.rs")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("pub mod client;\n\npub mod network;\n\n#[cfg(test)]\nmod tests {\n #[test]\n fn it_works() {\n assert_eq!(2 + 2, 4);\n }\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第十一章会更详细的解释测试,不过其中部分内容现在应该可以理解了:有一个叫做 "),s("code",[this._v("tests")]),this._v(" 的模块紧邻其他模块,同时包含一个叫做 "),s("code",[this._v("it_works")]),this._v(" 的函数。即便存在一些特殊注解,"),s("code",[this._v("tests")]),this._v(" 也不过是另外一个模块!所以我们的模块层次结构看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("communicator\n ├── client\n ├── network\n | └── client\n └── tests\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("测试是为了检验库中的代码而存在的,所以让我们尝试在 "),s("code",[this._v("it_works")]),this._v(" 函数中调用 "),s("code",[this._v("client::connect")]),this._v(" 函数,即便现在不准备测试任何功能。这还不能工作:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("it_works")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n client"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("connect")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用 "),s("code",[this._v("cargo test")]),this._v(" 命令运行测试:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test\n Compiling communicator v0.1.0 (file:///projects/communicator)\nerror[E0433]: failed to resolve. Use of undeclared type or module `client`\n --\x3e src/lib.rs:9:9\n |\n9 | client::connect();\n | ^^^^^^ Use of undeclared type or module `client`\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("编译失败了,不过为什么呢?并不需要像 "),n("em",[t._v("src/main.rs")]),t._v(" 那样将 "),n("code",[t._v("communicator::")]),t._v(" 置于函数前,因为这里肯定是在 "),n("code",[t._v("communicator")]),t._v(" 库 crate 之内的。失败的原因是路径是相对于当前模块的,在这里就是 "),n("code",[t._v("tests")]),t._v("。唯一的例外就是 "),n("code",[t._v("use")]),t._v(" 语句,它默认是相对于 crate 根模块的。我们的 "),n("code",[t._v("tests")]),t._v(" 模块需要 "),n("code",[t._v("client")]),t._v(" 模块位于其作用域中!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("那么如何在模块层次结构中回退一级模块,以便在 "),s("code",[this._v("tests")]),this._v(" 模块中能够调用 "),s("code",[this._v("client::connect")]),this._v("函数呢?在 "),s("code",[this._v("tests")]),this._v(" 模块中,要么可以在开头使用双冒号来让 Rust 知道我们想要从根模块开始并列出整个路径:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("::client::connect();\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("要么可以使用 "),s("code",[this._v("super")]),this._v(" 在层级中上移到当前模块的上一级模块,如下:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("super::client::connect();\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在这个例子中这两个选择看不出有多么大的区别,不过随着模块层次的更加深入,每次都从根模块开始就会显得很长了。在这些情况下,使用 "),s("code",[this._v("super")]),this._v(" 来获取当前模块的同级模块是一个好的捷径。再加上,如果在代码中的很多地方指定了从根开始的路径,那么当通过移动子树或到其他位置来重新排列模块时,最终就需要更新很多地方的路径,这就非常乏味无趣了。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在每一个测试中总是不得不编写 "),n("code",[t._v("super::")]),t._v(" 也会显得很恼人,不过你已经见过解决这个问题的利器了:"),n("code",[t._v("use")]),t._v("!"),n("code",[t._v("super::")]),t._v(" 的功能改变了提供给 "),n("code",[t._v("use")]),t._v(" 的路径,使其不再相对于根模块而是相对于父模块。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为此,特别是在 "),s("code",[this._v("tests")]),this._v(" 模块,"),s("code",[this._v("use super::something")]),this._v(" 是常用的手段。所以现在的测试看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("super")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("client"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("it_works")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n client"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("connect")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果再次运行"),s("code",[this._v("cargo test")]),this._v(",测试将会通过而且测试结果输出的第一部分将会是:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test\n Compiling communicator v0.1.0 (file:///projects/communicator)\n Running target/debug/communicator-92007ddb5330fa5a\n\nrunning 1 test\ntest tests::it_works ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])}],!1,null,null,null);e.options.__file="ch07-03-importing-names-with-use.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/43.b3333fb7.js b/src/.vuepress/dist/assets/js/43.b3333fb7.js
new file mode 100644
index 0000000..b6332f3
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/43.b3333fb7.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[43],{227:function(t,e,n){"use strict";n.r(e);var r=n(0),o=Object(r.a)({},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch08-00-common-collections.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch08-00-common-collections.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 820ac357f6cf0e866e5a8e7a9c57dd3e17e9f8ca")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),n("p",[t._v("对于标准库提供的其他类型的集合,请查看"),n("a",{attrs:{href:"https://doc.rust-lang.org/std/collections",target:"_blank",rel:"noopener noreferrer"}},[t._v("文档"),n("OutboundLink")],1),t._v("。")]),t._v(" "),n("p",[t._v("我们将讨论如何创建和更新 vector、字符串和哈希 map,以及它们有什么特别之处。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"通用集合类型"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#通用集合类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 通用集合类型")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Rust 标准库中包含一系列被称为 "),e("strong",[this._v("集合")]),this._v("("),e("em",[this._v("collections")]),this._v(")的非常有用的数据结构。大部分其他数据类型都代表一个特定的值,不过集合可以包含多个值。不同于内建的数组和元组类型,这些集合指向的数据是储存在堆上的,这意味着数据的数量不必在编译时就已知并且可以随着程序的运行增长或缩小。每种集合都有着不同能力和代价,而为所处的场景选择合适的集合则是你将要始终成长的技能。在这一章里,我们将详细的了解三个在 Rust 程序中被广泛使用的集合:")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("ul",[n("li",[n("em",[t._v("vector")]),t._v(" 允许我们一个挨着一个地储存一系列数量可变的值")]),t._v(" "),n("li",[n("strong",[t._v("字符串")]),t._v("("),n("em",[t._v("string")]),t._v(")是一个字符的集合。我们之前见过 "),n("code",[t._v("String")]),t._v(" 类型,不过在本章我们将深入了解。")]),t._v(" "),n("li",[n("strong",[t._v("哈希 map")]),t._v("("),n("em",[t._v("hash map")]),t._v(")允许我们将值与一个特定的键(key)相关联。这是一个叫做 "),n("em",[t._v("map")]),t._v(" 的更通用的数据结构的特定实现。")])])}],!1,null,null,null);o.options.__file="ch08-00-common-collections.md";e.default=o.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/44.97c13303.js b/src/.vuepress/dist/assets/js/44.97c13303.js
new file mode 100644
index 0000000..f5e4003
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/44.97c13303.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[44],{226:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch08-01-vectors.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch08-01-vectors.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._m(19),t._v(" "),a("p",[t._v("当 vector 被丢弃时,所有其内容也会被丢弃,这意味着这里它包含的整数将被清理。这可能看起来非常直观,不过一旦开始使用 vector 元素的引用,情况就变得有些复杂了。下面让我们处理这种情况!")]),t._v(" "),t._m(20),t._v(" "),a("p",[t._v("现在你知道如何创建、更新和销毁 vector 了,接下来的一步最好了解一下如何读取它们的内容。有两种方法引用 vector 中储存的值。为了更加清楚的说明这个例子,我们标注这些函数返回的值的类型。")]),t._v(" "),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._v(" "),a("p",[t._v("Rust 有两个引用元素的方法的原因是程序可以选择如何处理当索引值在 vector 中没有对应值的情况。作为一个例子,让我们看看如果有一个有五个元素的 vector 接着尝试访问索引为 100 的元素时程序会如何处理,如示例 8-6 所示:")]),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),a("p",[t._v("一旦程序获取了一个有效的引用,借用检查器将会执行所有权和借用规则(第四章讲到)来确保 vector 内容的这个引用和任何其他引用保持有效。回忆一下不能在相同作用域中同时存在可变和不可变引用的规则。这个规则适用于示例 8-7,当我们获取了 vector 的第一个元素的不可变引用并尝试在 vector 末尾增加一个元素的时候,这是行不通的:")]),t._v(" "),t._m(29),t._m(30),t._v(" "),a("p",[t._v("编译会给出这个错误:")]),t._v(" "),t._m(31),a("p",[t._v("示例 8-7 中的代码看起来应该能够运行:为什么第一个元素的引用会关心 vector 结尾的变化?不能这么做的原因是由于 vector 的工作方式:在 vector 的结尾增加新元素时,在没有足够空间将所有所有元素依次相邻存放的情况下,可能会要求分配新内存并将老的元素拷贝到新的空间中。这时,第一个元素的引用就指向了被释放的内存。借用规则阻止程序陷入这种状况。")]),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),a("p",[t._v("在本章的开始,我们提到 vector 只能储存相同类型的值。这是很不方便的;绝对会有需要储存一系列不同类型的值的用例。幸运的是,枚举的成员都被定义为相同的枚举类型,所以当需要在 vector 中储存不同类型值时,我们可以定义并使用一个枚举!")]),t._v(" "),a("p",[t._v("例如,假如我们想要从电子表格的一行中获取值,而这一行的有些列包含数字,有些包含浮点值,还有些是字符串。我们可以定义一个枚举,其成员会存放这些不同类型的值,同时所有这些枚举成员都会被当作相同类型,那个枚举的类型。接着可以创建一个储存枚举值的 vector,这样最终就能够储存不同类型的值了。示例 8-10 展示了其用例:")]),t._v(" "),t._m(42),t._m(43),t._v(" "),t._m(44),t._v(" "),a("p",[t._v("如果在编写程序时不能确切无遗地知道运行时会储存进 vector 的所有类型,枚举技术就行不通了。相反,你可以使用 trait 对象,第十七章会讲到它。")]),t._v(" "),t._m(45)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"vector-用来储存一系列的值"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#vector-用来储存一系列的值","aria-hidden":"true"}},[this._v("#")]),this._v(" vector 用来储存一系列的值")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们要讲到的第一个类型是 "),s("code",[this._v("Vec")]),this._v(",也被称为 "),s("em",[this._v("vector")]),this._v("。vector 允许我们在一个单独的数据结构中储存多于一个的值,它在内存中彼此相邻地排列所有的值。vector 只能储存相同类型的值。它们在拥有一系列项的场景下非常实用,例如文件中的文本行或是购物车中商品的价格。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"新建-vector"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#新建-vector","aria-hidden":"true"}},[this._v("#")]),this._v(" 新建 vector")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为了创建一个新的空 vector,可以调用 "),s("code",[this._v("Vec::new")]),this._v(" 函数,如示例 8-1 所示:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("i32"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Vec"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-1:新建一个空的 vector 来储存 "),s("code",[this._v("i32")]),this._v(" 类型的值")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("注意这里我们增加了一个类型注解。因为没有向这个 vector 中插入任何值,Rust 并不知道我们想要储存什么类型的元素。这是一个非常重要的点。vector 是用泛型实现的,第十章会涉及到如何对你自己的类型使用它们。现在,所有你需要知道的就是 "),a("code",[t._v("Vec")]),t._v(" 是一个由标准库提供的类型,它可以存放任何类型,而当 "),a("code",[t._v("Vec")]),t._v(" 存放某个特定类型时,那个类型位于尖括号中。在示例 8-1 中,我们告诉 Rust "),a("code",[t._v("v")]),t._v(" 这个 "),a("code",[t._v("Vec")]),t._v(" 将存放 "),a("code",[t._v("i32")]),t._v(" 类型的元素。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("在更实际的代码中,一旦插入值 Rust 就可以推断出想要存放的类型,所以你很少会需要这些类型注解。更常见的做法是使用初始值来创建一个 "),a("code",[t._v("Vec")]),t._v(",而且为了方便 Rust 提供了 "),a("code",[t._v("vec!")]),t._v(" 宏。这个宏会根据我们提供的值来创建一个新的 "),a("code",[t._v("Vec")]),t._v("。示例 8-2 新建一个拥有值 "),a("code",[t._v("1")]),t._v("、"),a("code",[t._v("2")]),t._v(" 和 "),a("code",[t._v("3")]),t._v(" 的 "),a("code",[t._v("Vec")]),t._v(":")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-2:新建一个包含初值的 vector")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为我们提供了 "),s("code",[this._v("i32")]),this._v(" 类型的初始值,Rust 可以推断出 "),s("code",[this._v("v")]),this._v(" 的类型是 "),s("code",[this._v("Vec")]),this._v(",因此类型注解就不是必须的。接下来让我们看看如何修改一个 vector。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"更新-vector"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#更新-vector","aria-hidden":"true"}},[this._v("#")]),this._v(" 更新 vector")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("对于新建一个 vector 并向其增加元素,可以使用 "),s("code",[this._v("push")]),this._v(" 方法,如示例 8-3 所示:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" v "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Vec"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nv"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("push")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nv"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("push")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("6")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nv"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("push")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("7")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nv"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("push")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("8")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-3:使用 "),s("code",[this._v("push")]),this._v(" 方法向 vector 增加值")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如第三章中讨论的任何变量一样,如果想要能够改变它的值,必须使用 "),s("code",[this._v("mut")]),this._v(" 关键字使其可变。放入其中的所有值都是 "),s("code",[this._v("i32")]),this._v(" 类型的,而且 Rust 也根据数据做出如此判断,所以不需要 "),s("code",[this._v("Vec")]),this._v(" 注解。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"丢弃-vector-时也会丢弃其所有元素"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#丢弃-vector-时也会丢弃其所有元素","aria-hidden":"true"}},[this._v("#")]),this._v(" 丢弃 vector 时也会丢弃其所有元素")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("类似于任何其他的 "),s("code",[this._v("struct")]),this._v(",vector 在其离开作用域时会被释放,如示例 8-4 所标注的:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token comment"}},[t._v("// 处理变量 v")]),t._v("\n\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// <- 这里 v 离开作用域并被丢弃")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-4:展示 vector 和其元素于何处被丢弃")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"读取-vector-的元素"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#读取-vector-的元素","aria-hidden":"true"}},[this._v("#")]),this._v(" 读取 vector 的元素")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 8-5 展示了访问 vector 中一个值的两种方式,索引语法或者 "),s("code",[this._v("get")]),this._v(" 方法:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" third"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("i32 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("v"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"The third element is {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" third"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" v"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("get")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("Some")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("third"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"The third element is {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" third"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n None "),a("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"There is no third element."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("列表 8-5:使用索引语法或 "),s("code",[this._v("get")]),this._v(" 方法来访问 vector 中的项")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这里有两个需要注意的地方。首先,我们使用索引值 "),a("code",[t._v("2")]),t._v(" 来获取第三个元素,索引是从 0 开始的。其次,这两个不同的获取第三个元素的方式分别为:使用 "),a("code",[t._v("&")]),t._v(" 和 "),a("code",[t._v("[]")]),t._v(" 返回一个引用;或者使用 "),a("code",[t._v("get")]),t._v(" 方法以索引作为参数来返回一个 "),a("code",[t._v("Option<&T>")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,should_panic,panics extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("let v = vec![1, 2, 3, 4, 5];\n\nlet does_not_exist = &v[100];\nlet does_not_exist = v.get(100);\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-6:尝试访问一个包含 5 个元素的 vector 的索引 100 处的元素")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当运行这段代码,你会发现对于第一个 "),s("code",[this._v("[]")]),this._v(" 方法,当引用一个不存在的元素时 Rust 会造成 panic。这个方法更适合当程序认为尝试访问超过 vector 结尾的元素是一个严重错误的情况,这时应该使程序崩溃。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("当 "),a("code",[t._v("get")]),t._v(" 方法被传递了一个数组外的索引时,它不会 panic 而是返回 "),a("code",[t._v("None")]),t._v("。当偶尔出现超过 vector 范围的访问属于正常情况的时候可以考虑使用它。接着你的代码可以有处理 "),a("code",[t._v("Some(&element)")]),t._v(" 或 "),a("code",[t._v("None")]),t._v(" 的逻辑,如第六章讨论的那样。例如,索引可能来源于用户输入的数字。如果它们不慎输入了一个过大的数字那么程序就会得到 "),a("code",[t._v("None")]),t._v(" 值,你可以告诉用户当前 vector 元素的数量并再请求它们输入一个有效的值。这就比因为输入错误而使程序崩溃要友好的多!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let mut v = vec![1, 2, 3, 4, 5];\n\nlet first = &v[0];\n\nv.push(6);\n\nprintln!("The first element is: {}", first);\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-7:在拥有 vector 中项的引用的同时向其增加一个元素")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable\n --\x3e src/main.rs:10:5\n |\n8 | let first = &v[0];\n | - immutable borrow occurs here\n9 |\n10 | v.push(6);\n | ^^^^^^^^^ mutable borrow occurs here\n11 |\n12 | println!("The first element is: {}", first);\n | ----- borrow later used here\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("blockquote",[s("p",[this._v("注意:关于 "),s("code",[this._v("Vec")]),this._v(" 类型的更多实现细节,在 "),s("em",[this._v("https://doc.rust-lang.org/stable/nomicon/vec.html")]),this._v(" 查看 “The Nomicon”")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"遍历-vector-中的元素"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#遍历-vector-中的元素","aria-hidden":"true"}},[this._v("#")]),this._v(" 遍历 vector 中的元素")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果想要依次访问 vector 中的每一个元素,我们可以遍历其所有的元素而无需通过索引一次一个的访问。示例 8-8 展示了如何使用 "),s("code",[this._v("for")]),this._v(" 循环来获取 "),s("code",[this._v("i32")]),this._v(" 值的 vector 中的每一个元素的不可变引用并将其打印:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("100")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("32")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("57")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("v "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-8:通过 "),s("code",[this._v("for")]),this._v(" 循环遍历 vector 的元素并打印")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们也可以遍历可变 vector 的每一个元素的可变引用以便能改变他们。示例 8-9 中的 "),s("code",[this._v("for")]),this._v(" 循环会给每一个元素加 "),s("code",[this._v("50")]),this._v(":")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" v "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("100")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("32")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("57")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" v "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v("i "),a("span",{attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例8-9:遍历 vector 中元素的可变引用")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("为了修改可变引用所指向的值,在使用 "),a("code",[t._v("+=")]),t._v(" 运算符之前必须使用解引用运算符("),a("code",[t._v("*")]),t._v(")获取 "),a("code",[t._v("i")]),t._v(" 中的值。第十五章会详细介绍 "),a("code",[t._v("*")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用枚举来储存多种类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用枚举来储存多种类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用枚举来储存多种类型")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" SpreadsheetCell "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("Int")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("Float")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f64"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("Text")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" row "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n SpreadsheetCell"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("Int")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n SpreadsheetCell"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("Text")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n SpreadsheetCell"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("Float")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("10.12")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-10:定义一个枚举,以便能在 vector 中存放不同类型的数据")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 在编译时就必须准确的知道 vector 中类型的原因在于它需要知道储存每个元素到底需要多少内存。第二个好处是可以准确的知道这个 vector 中允许什么类型。如果 Rust 允许 vector 存放任意类型,那么当对 vector 元素执行操作时一个或多个类型的值就有可能会造成错误。使用枚举外加 "),s("code",[this._v("match")]),this._v(" 意味着 Rust 能在编译时就保证总是会处理所有可能的情况,正如第六章讲到的那样。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("现在我们了解了一些使用 vector 的最常见的方式,请一定去看看标准库中 "),a("code",[t._v("Vec")]),t._v(" 定义的很多其他实用方法的 API 文档。例如,除了 "),a("code",[t._v("push")]),t._v(" 之外还有一个 "),a("code",[t._v("pop")]),t._v(" 方法,它会移除并返回 vector 的最后一个元素。让我们继续下一个集合类型:"),a("code",[t._v("String")]),t._v("!")])}],!1,null,null,null);e.options.__file="ch08-01-vectors.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/45.8afb63d1.js b/src/.vuepress/dist/assets/js/45.8afb63d1.js
new file mode 100644
index 0000000..d3d1ed1
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/45.8afb63d1.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[45],{225:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch08-02-strings.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch08-02-strings.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit a86c1d315789b3ca13b20d50ad5005c62bdd9e37")])]),t._v(" "),a("p",[t._v("第四章已经讲过一些字符串的内容,不过现在让我们更深入地了解它。字符串是新晋 Rustacean 们通常会被困住的领域,这是由于三方面理由的结合:Rust 倾向于确保暴露出可能的错误,字符串是比很多程序员所想象的要更为复杂的数据结构,以及 UTF-8。所有这些要素结合起来对于来自其他语言背景的程序员就可能显得很困难了。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),a("p",[t._v("记住字符串是 UTF-8 编码的,所以可以包含任何可以正确编码的数据,如示例 8-14 所示。")]),t._v(" "),t._m(18),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._m(47),t._v(" "),t._m(48),t._m(49),t._v(" "),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._m(53),t._v(" "),a("p",[t._v("会导致如下错误:")]),t._v(" "),t._m(54),a("p",[t._v("错误和提示说明了全部问题:Rust 的字符串不支持索引。那么接下来的问题是,为什么不支持呢?为了回答这个问题,我们必须先聊一聊 Rust 是如何在内存中储存字符串的。")]),t._v(" "),t._m(55),t._v(" "),t._m(56),t._v(" "),t._m(57),t._m(58),t._v(" "),t._m(59),a("p",[t._v("当问及这个字符是多长的时候有人可能会说是 12。然而,Rust 的回答是 24。这是使用 UTF-8 编码 “Здравствуйте” 所需要的字节数,这是因为每个 Unicode 标量值需要两个字节存储。因此一个字符串字节值的索引并不总是对应一个有效的 Unicode 标量值。作为演示,考虑如下无效的 Rust 代码:")]),t._v(" "),t._m(60),t._m(61),t._v(" "),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._v(" "),t._m(65),t._m(66),t._v(" "),t._m(67),t._m(68),t._v(" "),t._m(69),a("p",[t._v("Rust 提供了多种不同的方式来解释计算机储存的原始字符串数据,这样程序就可以选择它需要的表现方式,而无所谓是何种人类语言。")]),t._v(" "),t._m(70),t._v(" "),t._m(71),t._v(" "),t._m(72),t._v(" "),t._m(73),t._m(74),t._v(" "),t._m(75),t._v(" "),t._m(76),a("p",[t._v("你应该小心谨慎的使用这个操作,因为这么做可能会使你的程序崩溃。")]),t._v(" "),t._m(77),t._v(" "),a("p",[t._v("幸运的是,这里还有其他获取字符串元素的方式。")]),t._v(" "),t._m(78),t._v(" "),t._m(79),a("p",[t._v("这些代码会打印出如下内容:")]),t._v(" "),t._m(80),t._m(81),t._v(" "),t._m(82),t._m(83),t._v(" "),t._m(84),a("p",[t._v("不过请记住有效的 Unicode 标量值可能会由不止一个字节组成。")]),t._v(" "),a("p",[t._v("从字符串中获取字形簇是很复杂的,所以标准库并没有提供这个功能。"),a("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),a("OutboundLink")],1),t._v(" 上有些提供这样功能的 crate。")]),t._v(" "),t._m(85),t._v(" "),t._m(86),t._v(" "),a("p",[t._v("现在让我们转向一些不太复杂的集合:哈希 map!")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"使用字符串存储-utf-8-编码的文本"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用字符串存储-utf-8-编码的文本","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用字符串存储 UTF-8 编码的文本")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("在集合章节中讨论字符串的原因是,字符串就是作为字节的集合外加一些方法实现的,当这些字节被解释为文本时,这些方法提供了实用的功能。在这一部分,我们会讲到 "),a("code",[t._v("String")]),t._v(" 中那些任何集合类型都有的操作,比如创建、更新和读取。也会讨论 "),a("code",[t._v("String")]),t._v(" 与其他集合不一样的地方,例如索引"),a("code",[t._v("String")]),t._v(" 是很复杂的,由于人和计算机理解 "),a("code",[t._v("String")]),t._v(" 数据方式的不同。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"什么是字符串?"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#什么是字符串?","aria-hidden":"true"}},[this._v("#")]),this._v(" 什么是字符串?")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("在开始深入这些方面之前,我们需要讨论一下术语 "),a("strong",[t._v("字符串")]),t._v(" 的具体意义。Rust 的核心语言中只有一种字符串类型:"),a("code",[t._v("str")]),t._v(",字符串 slice,它通常以被借用的形式出现,"),a("code",[t._v("&str")]),t._v("。第四章讲到了 "),a("strong",[t._v("字符串 slice")]),t._v(":它们是一些储存在别处的 UTF-8 编码字符串数据的引用。比如字符串字面值被储存在程序的二进制输出中,字符串 slice 也是如此。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("称作 "),a("code",[t._v("String")]),t._v(" 的类型是由标准库提供的,而没有写进核心语言部分,它是可增长的、可变的、有所有权的、UTF-8 编码的字符串类型。当 Rustacean 们谈到 Rust 的 “字符串”时,它们通常指的是 "),a("code",[t._v("String")]),t._v(" 和字符串 slice "),a("code",[t._v("&str")]),t._v(" 类型,而不仅仅是其中之一。虽然本部分内容大多是关于 "),a("code",[t._v("String")]),t._v(" 的,不过这两个类型在 Rust 标准库中都被广泛使用,"),a("code",[t._v("String")]),t._v(" 和字符串 slice 都是 UTF-8 编码的。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("Rust 标准库中还包含一系列其他字符串类型,比如 "),a("code",[t._v("OsString")]),t._v("、"),a("code",[t._v("OsStr")]),t._v("、"),a("code",[t._v("CString")]),t._v(" 和 "),a("code",[t._v("CStr")]),t._v("。相关库 crate 甚至会提供更多储存字符串数据的选择。看到这些由 "),a("code",[t._v("String")]),t._v(" 或是 "),a("code",[t._v("Str")]),t._v(" 结尾的名字了吗?这对应着它们提供的所有权和可借用的字符串变体,就像是你之前看到的 "),a("code",[t._v("String")]),t._v(" 和 "),a("code",[t._v("str")]),t._v("。举例而言,这些字符串类型能够以不同的编码或内存表现形式上以不同的形式存储文本内容。本章将不会讨论其他这些字符串类型,查看 API 文档来更多的了解如何使用它们以及各自适合的场景。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"新建字符串"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#新建字符串","aria-hidden":"true"}},[this._v("#")]),this._v(" 新建字符串")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("很多 "),s("code",[this._v("Vec")]),this._v(" 可用的操作在 "),s("code",[this._v("String")]),this._v(" 中同样可用,从以 "),s("code",[this._v("new")]),this._v(" 函数创建字符串开始,如示例 8-11 所示。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-11:新建一个空的 "),s("code",[this._v("String")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这新建了一个叫做 "),s("code",[this._v("s")]),this._v(" 的空的字符串,接着我们可以向其中装载数据。通常字符串会有初始数据,因为我们希望一开始就有这个字符串。为此,可以使用 "),s("code",[this._v("to_string")]),this._v(" 方法,它能用于任何实现了 "),s("code",[this._v("Display")]),this._v(" trait 的类型,字符串字面值也实现了它。示例 8-12 展示了两个例子。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" data "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"initial contents"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" data"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("to_string")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token comment"}},[t._v("// 该方法也可直接用于字符串字面值:")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"initial contents"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("to_string")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-12:使用 "),s("code",[this._v("to_string")]),this._v(" 方法从字符串字面值创建 "),s("code",[this._v("String")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这些代码会创建包含 "),s("code",[this._v("initial contents")]),this._v(" 的字符串。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("也可以使用 "),s("code",[this._v("String::from")]),this._v(" 函数来从字符串字面值创建 "),s("code",[this._v("String")]),this._v("。示例 8-13 中的代码代码等同于使用 "),s("code",[this._v("to_string")]),this._v("。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"initial contents"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-13:使用 "),s("code",[this._v("String::from")]),this._v(" 函数从字符串字面值创建 "),s("code",[this._v("String")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为字符串应用广泛,这里有很多不同的用于字符串的通用 API 可供选择。它们有些可能显得有些多余,不过都有其用武之地!在这个例子中,"),s("code",[this._v("String::from")]),this._v(" 和 "),s("code",[this._v(".to_string")]),this._v(" 最终做了完全相同的工作,所以如何选择就是风格问题了。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"السلام عليكم"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Dobrý den"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Hello"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"שָׁלוֹם"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"नमस्ते"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"こんにちは"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"안녕하세요"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"你好"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Olá"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Здравствуйте"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Hola"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-14:在字符串中储存不同语言的问候语")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("所有这些都是有效的 "),s("code",[this._v("String")]),this._v(" 值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"更新字符串"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#更新字符串","aria-hidden":"true"}},[this._v("#")]),this._v(" 更新字符串")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("String")]),t._v(" 的大小可以增长其内容也可以改变,就像可以放入更多数据来改变 "),a("code",[t._v("Vec")]),t._v(" 的内容一样。另外,可以方便的使用 "),a("code",[t._v("+")]),t._v(" 运算符或 "),a("code",[t._v("format!")]),t._v(" 宏来拼接 "),a("code",[t._v("String")]),t._v(" 值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用-push-str-和-push-附加字符串"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-push-str-和-push-附加字符串","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("push_str")]),this._v(" 和 "),s("code",[this._v("push")]),this._v(" 附加字符串")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以通过 "),s("code",[this._v("push_str")]),this._v(" 方法来附加字符串 slice,从而使 "),s("code",[this._v("String")]),this._v(" 变长,如示例 8-15 所示。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"foo"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\ns"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("push_str")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"bar"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-15:使用 "),s("code",[this._v("push_str")]),this._v(" 方法向 "),s("code",[this._v("String")]),this._v(" 附加字符串 slice")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("执行这两行代码之后 "),a("code",[t._v("s")]),t._v(" 将会包含 "),a("code",[t._v("foobar")]),t._v("。"),a("code",[t._v("push_str")]),t._v(" 方法获取字符串 slice,因为我们并不需要获取参数的所有权。例如,示例 8-16 展示了如果将 "),a("code",[t._v("s2")]),t._v(" 的内容附加到 "),a("code",[t._v("s1")]),t._v(" 中后自身不能被使用就糟糕了。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"foo"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s2 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"bar"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\ns1"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("push_str")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s2"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"s2 is {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s2"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-16:将字符串 slice 的内容附加到 "),s("code",[this._v("String")]),this._v(" 后使用它")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果 "),s("code",[this._v("push_str")]),this._v(" 方法获取了 "),s("code",[this._v("s2")]),this._v(" 的所有权,就不能在最后一行打印出其值了。好在代码如我们期望那样工作!")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("push")]),t._v(" 方法被定义为获取一个单独的字符作为参数,并附加到 "),a("code",[t._v("String")]),t._v(" 中。示例 8-17 展示了使用 "),a("code",[t._v("push")]),t._v(" 方法将字母 "),a("em",[t._v("l")]),t._v(" 加入 "),a("code",[t._v("String")]),t._v(" 的代码。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"lo"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\ns"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("push")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token char string"}},[t._v("'l'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-17:使用 "),s("code",[this._v("push")]),this._v(" 将一个字符加入 "),s("code",[this._v("String")]),this._v(" 值中")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("执行这些代码之后,"),s("code",[this._v("s")]),this._v(" 将会包含 “lol”。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用-运算符或-format-宏拼接字符串"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-运算符或-format-宏拼接字符串","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("+")]),this._v(" 运算符或 "),s("code",[this._v("format!")]),this._v(" 宏拼接字符串")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("通常你会希望将两个已知的字符串合并在一起。一种办法是像这样使用 "),s("code",[this._v("+")]),this._v(" 运算符,如示例 8-18 所示。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Hello, "')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s2 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"world!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s3 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s1 "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s2"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{attrs:{class:"token comment"}},[t._v("// 注意 s1 被移动了,不能继续使用")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-18:使用 "),s("code",[this._v("+")]),this._v(" 运算符将两个 "),s("code",[this._v("String")]),this._v(" 值合并到一个新的 "),s("code",[this._v("String")]),this._v(" 值中")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("执行完这些代码之后字符串 "),a("code",[t._v("s3")]),t._v(" 将会包含 "),a("code",[t._v("Hello, world!")]),t._v("。"),a("code",[t._v("s1")]),t._v(" 在相加后不再有效的原因,和使用 "),a("code",[t._v("s2")]),t._v(" 的引用的原因与使用 "),a("code",[t._v("+")]),t._v(" 运算符时调用的方法签名有关,这个函数签名看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn add(self, s: &str) -> String {\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这并不是标准库中实际的签名;标准库中的 "),a("code",[t._v("add")]),t._v(" 使用泛型定义。这里我们看到的 "),a("code",[t._v("add")]),t._v(" 的签名使用具体类型代替了泛型,这也正是当使用 "),a("code",[t._v("String")]),t._v(" 值调用这个方法会发生的。第十章会讨论泛型。这个签名提供了理解 "),a("code",[t._v("+")]),t._v(" 运算那微妙部分的线索。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("首先,"),a("code",[t._v("s2")]),t._v(" 使用了 "),a("code",[t._v("&")]),t._v(",意味着我们使用第二个字符串的 "),a("strong",[t._v("引用")]),t._v(" 与第一个字符串相加。这是因为 "),a("code",[t._v("add")]),t._v(" 函数的 "),a("code",[t._v("s")]),t._v(" 参数:只能将 "),a("code",[t._v("&str")]),t._v(" 和 "),a("code",[t._v("String")]),t._v(" 相加,不能将两个 "),a("code",[t._v("String")]),t._v(" 值相加。不过等一下 —— 正如 "),a("code",[t._v("add")]),t._v(" 的第二个参数所指定的,"),a("code",[t._v("&s2")]),t._v(" 的类型是 "),a("code",[t._v("&String")]),t._v(" 而不是 "),a("code",[t._v("&str")]),t._v("。那么为什么示例 8-18 还能编译呢?")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("之所以能够在 "),a("code",[t._v("add")]),t._v(" 调用中使用 "),a("code",[t._v("&s2")]),t._v(" 是因为 "),a("code",[t._v("&String")]),t._v(" 可以被 "),a("strong",[t._v("强转")]),t._v("("),a("em",[t._v("coerced")]),t._v(")成 "),a("code",[t._v("&str")]),t._v("。当"),a("code",[t._v("add")]),t._v("函数被调用时,Rust 使用了一个被称为 "),a("strong",[t._v("解引用强制多态")]),t._v("("),a("em",[t._v("deref coercion")]),t._v(")的技术,你可以将其理解为它把 "),a("code",[t._v("&s2")]),t._v(" 变成了 "),a("code",[t._v("&s2[..]")]),t._v("。第十五章会更深入的讨论解引用强制多态。因为 "),a("code",[t._v("add")]),t._v(" 没有获取参数的所有权,所以 "),a("code",[t._v("s2")]),t._v(" 在这个操作后仍然是有效的 "),a("code",[t._v("String")]),t._v("。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("其次,可以发现签名中 "),a("code",[t._v("add")]),t._v(" 获取了 "),a("code",[t._v("self")]),t._v(" 的所有权,因为 "),a("code",[t._v("self")]),t._v(" "),a("strong",[t._v("没有")]),t._v(" 使用 "),a("code",[t._v("&")]),t._v("。这意味着示例 8-18 中的 "),a("code",[t._v("s1")]),t._v(" 的所有权将被移动到 "),a("code",[t._v("add")]),t._v(" 调用中,之后就不再有效。所以虽然 "),a("code",[t._v("let s3 = s1 + &s2;")]),t._v(" 看起来就像它会复制两个字符串并创建一个新的字符串,而实际上这个语句会获取 "),a("code",[t._v("s1")]),t._v(" 的所有权,附加上从 "),a("code",[t._v("s2")]),t._v(" 中拷贝的内容,并返回结果的所有权。换句话说,它看起来好像生成了很多拷贝不过实际上并没有:这个实现比拷贝要更高效。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果想要级联多个字符串,"),s("code",[this._v("+")]),this._v(" 的行为就显得笨重了:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"tic"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s2 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"tac"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s3 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"toe"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s1 "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"-"')]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s2 "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"-"')]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s3"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这时 "),a("code",[t._v("s")]),t._v(" 的内容会是 “tic-tac-toe”。在有这么多 "),a("code",[t._v("+")]),t._v(" 和 "),a("code",[t._v('"')]),t._v(" 字符的情况下,很难理解具体发生了什么。对于更为复杂的字符串链接,可以使用 "),a("code",[t._v("format!")]),t._v(" 宏:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s1 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"tic"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s2 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"tac"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s3 "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"toe"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("format!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}-{}-{}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s1"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s2"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s3"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这些代码也会将 "),a("code",[t._v("s")]),t._v(" 设置为 “tic-tac-toe”。"),a("code",[t._v("format!")]),t._v(" 与 "),a("code",[t._v("println!")]),t._v(" 的工作原理相同,不过不同于将输出打印到屏幕上,它返回一个带有结果内容的 "),a("code",[t._v("String")]),t._v("。这个版本就好理解的多,并且不会获取任何参数的所有权。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"索引字符串"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#索引字符串","aria-hidden":"true"}},[this._v("#")]),this._v(" 索引字符串")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在很多语言中,通过索引来引用字符串中的单独字符是有效且常见的操作。然而在 Rust 中,如果你尝试使用索引语法访问 "),s("code",[this._v("String")]),this._v(" 的一部分,会出现一个错误。考虑一下如示例 8-19 中所示的无效代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let s1 = String::from("hello");\nlet h = s1[0];\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-19:尝试对字符串使用索引语法")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0277]: the trait bound `std::string::String: std::ops::Index<{integer}>` is not satisfied\n --\x3e\n |\n3 | let h = s1[0];\n | ^^^^^ the type `std::string::String` cannot be indexed by `{integer}`\n |\n = help: the trait `std::ops::Index<{integer}>` is not implemented for `std::string::String`\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"内部表现"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#内部表现","aria-hidden":"true"}},[this._v("#")]),this._v(" 内部表现")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("String")]),this._v(" 是一个 "),s("code",[this._v("Vec")]),this._v(" 的封装。让我们看看之前一些正确编码的字符串的例子。首先是这一个:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" len "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Hola"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("len")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在这里,"),s("code",[this._v("len")]),this._v(" 的值是 4 ,这意味着储存字符串 “Hola” 的 "),s("code",[this._v("Vec")]),this._v(" 的长度是四个字节:这里每一个字母的 UTF-8 编码都占用一个字节。那下面这个例子又如何呢?(注意这个字符串中的首字母是西里尔字母的 Ze 而不是阿拉伯数字 3 。)")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" len "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Здравствуйте"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("len")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let hello = "Здравствуйте";\nlet answer = &hello[0];\n')])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("answer")]),t._v(" 的值应该是什么呢?它应该是第一个字符 "),a("code",[t._v("З")]),t._v(" 吗?当使用 UTF-8 编码时,"),a("code",[t._v("З")]),t._v(" 的第一个字节 "),a("code",[t._v("208")]),t._v(",第二个是 "),a("code",[t._v("151")]),t._v(",所以 "),a("code",[t._v("answer")]),t._v(" 实际上应该是 "),a("code",[t._v("208")]),t._v(",不过 "),a("code",[t._v("208")]),t._v(" 自身并不是一个有效的字母。返回 "),a("code",[t._v("208")]),t._v(" 可不是一个请求字符串第一个字母的人所希望看到的,不过它是 Rust 在字节索引 0 位置所能提供的唯一数据。用户通常不会想要一个字节值被返回,即便这个字符串只有拉丁字母: 即便 "),a("code",[t._v('&"hello"[0]')]),t._v(" 是返回字节值的有效代码,它也应当返回 "),a("code",[t._v("104")]),t._v(" 而不是 "),a("code",[t._v("h")]),t._v("。为了避免返回意想不到值并造成不能立刻发现的 bug。Rust 选择不编译这些代码并及早杜绝了误会的发生。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"字节、标量值和字形簇!天呐!"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#字节、标量值和字形簇!天呐!","aria-hidden":"true"}},[this._v("#")]),this._v(" 字节、标量值和字形簇!天呐!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这引起了关于 UTF-8 的另外一个问题:从 Rust 的角度来讲,事实上有三种相关方式可以理解字符串:字节、标量值和字形簇(最接近人们眼中 "),s("strong",[this._v("字母")]),this._v(" 的概念)。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("比如这个用梵文书写的印度语单词 “नमस्ते”,最终它储存在 vector 中的 "),s("code",[this._v("u8")]),this._v(" 值看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164,\n224, 165, 135]\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里有 18 个字节,也就是计算机最终会储存的数据。如果从 Unicode 标量值的角度理解它们,也就像 Rust 的 "),s("code",[this._v("char")]),this._v(" 类型那样,这些字节看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("['न', 'म', 'स', '्', 'त', 'े']\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里有六个 "),s("code",[this._v("char")]),this._v(",不过第四个和第六个都不是字母,它们是发音符号本身并没有任何意义。最后,如果以字形簇的角度理解,就会得到人们所说的构成这个单词的四个字母:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('["न", "म", "स्", "ते"]\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("最后一个 Rust 不允许使用索引获取 "),s("code",[this._v("String")]),this._v(" 字符的原因是索引操作预期总是需要常数时间 (O(1))。但是对于 "),s("code",[this._v("String")]),this._v(" 不可能保证这样的性能,因为 Rust 不得不检查从字符串的开头到索引位置的内容来确定这里有多少有效的字符。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"字符串-slice"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#字符串-slice","aria-hidden":"true"}},[this._v("#")]),this._v(" 字符串 slice")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("索引字符串通常是一个坏点子,因为字符串索引应该返回的类型是不明确的:字节值、字符、字形簇或者字符串 slice。因此,如果你真的希望使用索引创建字符串 slice 时 Rust 会要求你更明确一些。为了更明确索引并表明你需要一个字符串 slice,相比使用 "),s("code",[this._v("[]")]),this._v(" 和单个值的索引,可以使用 "),s("code",[this._v("[]")]),this._v(" 和一个 range 来创建含特定字节的字符串 slice:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" hello "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"Здравствуйте"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("hello"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v("..")]),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里,"),s("code",[this._v("s")]),this._v(" 会是一个 "),s("code",[this._v("&str")]),this._v(",它包含字符串的头四个字节。早些时候,我们提到了这些字母都是两个字节长的,所以这意味着 "),s("code",[this._v("s")]),this._v(" 将会是 “Зд”。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果获取 "),s("code",[this._v("&hello[0..1]")]),this._v(" 会发生什么呢?答案是:在运行时会 panic,就跟访问 vector 中的无效索引时一样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("thread 'main' panicked at 'byte index 1 is not a char boundary; it is inside 'З' (bytes 0..2) of `Здравствуйте`', src/libcore/str/mod.rs:2188:4\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"遍历字符串的方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#遍历字符串的方法","aria-hidden":"true"}},[this._v("#")]),this._v(" 遍历字符串的方法")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果你需要操作单独的 Unicode 标量值,最好的选择是使用 "),s("code",[this._v("chars")]),this._v(" 方法。对 “नमस्ते” 调用 "),s("code",[this._v("chars")]),this._v(" 方法会将其分开并返回六个 "),s("code",[this._v("char")]),this._v(" 类型的值,接着就可以遍历其结果来访问每一个元素了:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" c "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"नमस्ते"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("chars")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("न\nम\nस\n्\nत\nे\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("bytes")]),this._v(" 方法返回每一个原始字节,这可能会适合你的使用场景:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" b "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"नमस्ते"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("bytes")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这些代码会打印出组成 "),s("code",[this._v("String")]),this._v(" 的 18 个字节:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("224\n164\n// --snip--\n165\n135\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"字符串并不简单"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#字符串并不简单","aria-hidden":"true"}},[this._v("#")]),this._v(" 字符串并不简单")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("总而言之,字符串还是很复杂的。不同的语言选择了不同的向程序员展示其复杂性的方式。Rust 选择了以准确的方式处理 "),s("code",[this._v("String")]),this._v(" 数据作为所有 Rust 程序的默认行为,这意味着程序员们必须更多的思考如何预先处理 UTF-8 数据。这种权衡取舍相比其他语言更多的暴露出了字符串的复杂性,不过也使你在开发生命周期后期免于处理涉及非 ASCII 字符的错误。")])}],!1,null,null,null);e.options.__file="ch08-02-strings.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/46.ac497cb6.js b/src/.vuepress/dist/assets/js/46.ac497cb6.js
new file mode 100644
index 0000000..e597f4d
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/46.ac497cb6.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[46],{224:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch08-03-hash-maps.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch08-03-hash-maps.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit d073ece693e880b69412e645e4eabe99e74e7590")])]),t._v(" "),t._m(1),t._v(" "),a("p",[t._v("哈希 map 可以用于需要任何类型作为键来寻找数据的情况,而不是像 vector 那样通过索引。例如,在一个游戏中,你可以将每个团队的分数记录到哈希 map 中,其中键是队伍的名字而值是每个队伍的分数。给出一个队名,就能得到他们的得分。")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),a("p",[t._v("如果将值的引用插入哈希 map,这些值本身将不会被移动进哈希 map。但是这些引用指向的值必须至少在哈希 map 有效时也是有效的。第十章 “使用生命周期保证引用有效” 部分将会更多的讨论这个问题。")]),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),a("p",[t._v("这会以任意顺序打印出每一个键值对:")]),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),a("p",[a("code",[t._v("HashMap")]),t._v(" 默认使用一种 “密码学安全的”(“cryptographically strong” )"),a("a",{attrs:{href:"%5Bhttps://www.131002.net/siphash/siphash.pdf%5D(https://www.131002.net/siphash/siphash.pdf)"}},[t._v("^siphash")]),t._v(" 哈希函数,它可以抵抗拒绝服务(Denial of Service, DoS)攻击。然而这并不是可用的最快的算法,不过为了更高的安全性值得付出一些性能的代价。如果性能监测显示此哈希函数非常慢,以致于你无法接受,你可以指定一个不同的 "),a("em",[t._v("hasher")]),t._v(" 来切换为其它函数。hasher 是一个实现了 "),a("code",[t._v("BuildHasher")]),t._v(" trait 的类型。第十章会讨论 trait 和如何实现它们。你并不需要从头开始实现你自己的 hasher;"),a("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),a("OutboundLink")],1),t._v(" 有其他人分享的实现了许多常用哈希算法的 hasher 的库。")]),t._v(" "),t._m(45),t._v(" "),a("p",[t._v("vector、字符串和哈希 map 会在你的程序需要储存、访问和修改数据时帮助你。这里有一些你应该能够解决的练习问题:")]),t._v(" "),t._m(46),t._v(" "),a("p",[t._v("标准库 API 文档中描述的这些类型的方法将有助于你进行这些练习!")]),t._v(" "),a("p",[t._v("我们已经开始接触可能会有失败操作的复杂程序了,这也意味着接下来是一个了解错误处理的绝佳时机!")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"哈希-map-储存键值对"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#哈希-map-储存键值对","aria-hidden":"true"}},[this._v("#")]),this._v(" 哈希 map 储存键值对")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("最后介绍的常用集合类型是 "),a("strong",[t._v("哈希 map")]),t._v("("),a("em",[t._v("hash map")]),t._v(")。"),a("code",[t._v("HashMap")]),t._v(" 类型储存了一个键类型 "),a("code",[t._v("K")]),t._v(" 对应一个值类型 "),a("code",[t._v("V")]),t._v(" 的映射。它通过一个 "),a("strong",[t._v("哈希函数")]),t._v("("),a("em",[t._v("hashing function")]),t._v(")来实现映射,决定如何将键和值放入内存中。很多编程语言支持这种数据结构,不过通常有不同的名字:哈希、map、对象、哈希表或者关联数组,仅举几例。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("本章我们会介绍哈希 map 的基本 API,不过还有更多吸引人的功能隐藏于标准库中 "),s("code",[this._v("HashMap")]),this._v(" 定义的函数中。一如既往请查看标准库文档来了解更多信息。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"新建一个哈希-map"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#新建一个哈希-map","aria-hidden":"true"}},[this._v("#")]),this._v(" 新建一个哈希 map")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以使用 "),s("code",[this._v("new")]),this._v(" 创建一个空的 "),s("code",[this._v("HashMap")]),this._v(",并使用 "),s("code",[this._v("insert")]),this._v(" 增加元素。这里我们记录两支队伍的分数,分别是蓝队和黄队。蓝队开始有 10 分而黄队开始有 50 分:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" scores "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("10")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Yellow"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-20:新建一个哈希 map 并插入一些键值对")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("注意必须首先 "),a("code",[t._v("use")]),t._v(" 标准库中集合部分的 "),a("code",[t._v("HashMap")]),t._v("。在这三个常用集合中,"),a("code",[t._v("HashMap")]),t._v(" 是最不常用的,所以并没有被 prelude 自动引用。标准库中对 "),a("code",[t._v("HashMap")]),t._v(" 的支持也相对较少,例如,并没有内建的构建宏。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("像 vector 一样,哈希 map 将它们的数据储存在堆上,这个 "),s("code",[this._v("HashMap")]),this._v(" 的键类型是 "),s("code",[this._v("String")]),this._v(" 而值类型是 "),s("code",[this._v("i32")]),this._v("。同样类似于 vector,哈希 map 是同质的:所有的键必须是相同类型,值也必须都是相同类型。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("另一个构建哈希 map 的方法是使用一个元组的 vector 的 "),a("code",[t._v("collect")]),t._v(" 方法,其中每个元组包含一个键值对。"),a("code",[t._v("collect")]),t._v(" 方法可以将数据收集进一系列的集合类型,包括 "),a("code",[t._v("HashMap")]),t._v("。例如,如果队伍的名字和初始分数分别在两个 vector 中,可以使用 "),a("code",[t._v("zip")]),t._v(" 方法来创建一个元组的 vector,其中 “Blue” 与 10 是一对,依此类推。接着就可以使用 "),a("code",[t._v("collect")]),t._v(" 方法将这个元组 vector 转换成一个 "),a("code",[t._v("HashMap")]),t._v(",如示例 8-21 所示:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" teams "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Yellow"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" initial_scores "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("10")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" scores"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" HashMap"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("_"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" teams"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("iter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("zip")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("initial_scores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("iter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("collect")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-21:用队伍列表和分数列表创建哈希 map")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里 "),s("code",[this._v("HashMap<_, _>")]),this._v(" 类型注解是必要的,因为可能 "),s("code",[this._v("collect")]),this._v(" 很多不同的数据结构,而除非显式指定否则 Rust 无从得知你需要的类型。但是对于键和值的类型参数来说,可以使用下划线占位,而 Rust 能够根据 vector 中数据的类型推断出 "),s("code",[this._v("HashMap")]),this._v(" 所包含的类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"哈希-map-和所有权"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#哈希-map-和所有权","aria-hidden":"true"}},[this._v("#")]),this._v(" 哈希 map 和所有权")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("对于像 "),s("code",[this._v("i32")]),this._v(" 这样的实现了 "),s("code",[this._v("Copy")]),this._v(" trait 的类型,其值可以拷贝进哈希 map。对于像 "),s("code",[this._v("String")]),this._v(" 这样拥有所有权的值,其值将被移动而哈希 map 会成为这些值的所有者,如示例 8-22 所示:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" field_name "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Favorite color"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" field_value "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" map "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nmap"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("field_name"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" field_value"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// 这里 field_name 和 field_value 不再有效,")]),t._v("\n"),a("span",{attrs:{class:"token comment"}},[t._v("// 尝试使用它们看看会出现什么编译错误!")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-22:展示一旦键值对被插入后就为哈希 map 所拥有")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当 "),s("code",[this._v("insert")]),this._v(" 调用将 "),s("code",[this._v("field_name")]),this._v(" 和 "),s("code",[this._v("field_value")]),this._v(" 移动到哈希 map 中后,将不能使用这两个绑定。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"访问哈希-map-中的值"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#访问哈希-map-中的值","aria-hidden":"true"}},[this._v("#")]),this._v(" 访问哈希 map 中的值")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以通过 "),s("code",[this._v("get")]),this._v(" 方法并提供对应的键来从哈希 map 中获取值,如示例 8-23 所示:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" scores "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("10")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Yellow"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" team_name "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" score "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" scores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("get")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("team_name"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-23:访问哈希 map 中储存的蓝队分数")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这里,"),a("code",[t._v("score")]),t._v(" 是与蓝队分数相关的值,应为 "),a("code",[t._v("Some(10)")]),t._v("。因为 "),a("code",[t._v("get")]),t._v(" 返回 "),a("code",[t._v("Option")]),t._v(",所以结果被装进 "),a("code",[t._v("Some")]),t._v(";如果某个键在哈希 map 中没有对应的值,"),a("code",[t._v("get")]),t._v(" 会返回 "),a("code",[t._v("None")]),t._v("。这时就要用某种第六章提到的方法之一来处理 "),a("code",[t._v("Option")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以使用与 vector 类似的方式来遍历哈希 map 中的每一个键值对,也就是 "),s("code",[this._v("for")]),this._v(" 循环:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" scores "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("10")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Yellow"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" value"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("scores "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}: {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" key"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" value"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("Yellow: 50\nBlue: 10\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"更新哈希-map"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#更新哈希-map","aria-hidden":"true"}},[this._v("#")]),this._v(" 更新哈希 map")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("尽管键值对的数量是可以增长的,不过任何时候,每个键只能关联一个值。当我们想要改变哈希 map 中的数据时,必须决定如何处理一个键已经有值了的情况。可以选择完全无视旧值并用新值代替旧值。可以选择保留旧值而忽略新值,并只在键 "),s("strong",[this._v("没有")]),this._v(" 对应值时增加新值。或者可以结合新旧两值。让我们看看这分别该如何处理!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"覆盖一个值"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#覆盖一个值","aria-hidden":"true"}},[this._v("#")]),this._v(" 覆盖一个值")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果我们插入了一个键值对,接着用相同的键插入一个不同的值,与这个键相关联的旧值将被替换。即便示例 8-24 中的代码调用了两次 "),s("code",[this._v("insert")]),this._v(",哈希 map 也只会包含一个键值对,因为两次都是对蓝队的键插入的值:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" scores "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("10")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("25")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{:?}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" scores"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-24:替换以特定键储存的值")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这会打印出 "),s("code",[this._v('{"Blue": 25}')]),this._v("。原始的值 "),s("code",[this._v("10")]),this._v(" 则被覆盖了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"只在键没有对应值时插入"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#只在键没有对应值时插入","aria-hidden":"true"}},[this._v("#")]),this._v(" 只在键没有对应值时插入")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们经常会检查某个特定的键是否有值,如果没有就插入一个值。为此哈希 map 有一个特有的 API,叫做 "),s("code",[this._v("entry")]),this._v(",它获取我们想要检查的键作为参数。"),s("code",[this._v("entry")]),this._v(" 函数的返回值是一个枚举,"),s("code",[this._v("Entry")]),this._v(",它代表了可能存在也可能不存在的值。比如说我们想要检查黄队的键是否关联了一个值。如果没有,就插入值 50,对于蓝队也是如此。使用 entry API 的代码看起来像示例 8-25 这样:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" scores "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("10")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("entry")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Yellow"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("or_insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("50")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nscores"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("entry")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Blue"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("or_insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("50")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{:?}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" scores"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-25:使用 "),s("code",[this._v("entry")]),this._v(" 方法只在键没有对应一个值时插入")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("Entry")]),t._v(" 的 "),a("code",[t._v("or_insert")]),t._v(" 方法在键对应的值存在时就返回这个值的 "),a("code",[t._v("Entry")]),t._v(",如果不存在则将参数作为新值插入并返回修改过的 "),a("code",[t._v("Entry")]),t._v("。这比编写自己的逻辑要简明的多,另外也与借用检查器结合得更好。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("运行示例 8-25 的代码会打印出 "),a("code",[t._v('{"Yellow": 50, "Blue": 10}')]),t._v("。第一个 "),a("code",[t._v("entry")]),t._v(" 调用会插入黄队的键和值 "),a("code",[t._v("50")]),t._v(",因为黄队并没有一个值。第二个 "),a("code",[t._v("entry")]),t._v(" 调用不会改变哈希 map 因为蓝队已经有了值 "),a("code",[t._v("10")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"根据旧值更新一个值"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#根据旧值更新一个值","aria-hidden":"true"}},[this._v("#")]),this._v(" 根据旧值更新一个值")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("另一个常见的哈希 map 的应用场景是找到一个键对应的值并根据旧的值更新它。例如,示例 8-26 中的代码计数一些文本中每一个单词分别出现了多少次。我们使用哈希 map 以单词作为键并递增其值来记录我们遇到过几次这个单词。如果是第一次看到某个单词,就插入值 "),s("code",[this._v("0")]),this._v("。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("collections"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" text "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token string"}},[t._v('"hello world wonderful world"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" map "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" HashMap"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" word "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" text"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("split_whitespace")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" count "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" map"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("entry")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("word"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("or_insert")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v("count "),a("span",{attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{:?}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" map"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 8-26:通过哈希 map 储存单词和计数来统计出现次数")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这会打印出 "),a("code",[t._v('{"world": 2, "hello": 1, "wonderful": 1}')]),t._v(","),a("code",[t._v("or_insert")]),t._v(" 方法事实上会返回这个键的值的一个可变引用("),a("code",[t._v("&mut V")]),t._v(")。这里我们将这个可变引用储存在 "),a("code",[t._v("count")]),t._v(" 变量中,所以为了赋值必须首先使用星号("),a("code",[t._v("*")]),t._v(")解引用 "),a("code",[t._v("count")]),t._v("。这个可变引用在 "),a("code",[t._v("for")]),t._v(" 循环的结尾离开作用域,这样所有这些改变都是安全的并符合借用规则。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"哈希函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#哈希函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 哈希函数")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[this._v("给定一系列数字,使用 vector 并返回这个列表的平均数(mean, average)、中位数(排列数组后位于中间的值)和众数(mode,出现次数最多的值;这里哈希函数会很有帮助)。")]),this._v(" "),s("li",[this._v("将字符串转换为 Pig Latin,也就是每一个单词的第一个辅音字母被移动到单词的结尾并增加 “ay”,所以 “first” 会变成 “irst-fay”。元音字母开头的单词则在结尾增加 “hay”(“apple” 会变成 “apple-hay”)。牢记 UTF-8 编码!")]),this._v(" "),s("li",[this._v("使用哈希 map 和 vector,创建一个文本接口来允许用户向公司的部门中增加员工的名字。例如,“Add Sally to Engineering” 或 “Add Amir to Sales”。接着让用户获取一个部门的所有员工的列表,或者公司每个部门的所有员工按照字母顺排序的列表。")])])}],!1,null,null,null);e.options.__file="ch08-03-hash-maps.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/47.dc1178aa.js b/src/.vuepress/dist/assets/js/47.dc1178aa.js
new file mode 100644
index 0000000..9e24c60
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/47.dc1178aa.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[47],{223:function(e,t,r){"use strict";r.r(t);var n=r(0),_=Object(n.a)({},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"content"},[e._m(0),e._v(" "),r("blockquote",[r("p",[r("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch09-00-error-handling.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ch09-00-error-handling.md"),r("OutboundLink")],1),e._v(" "),r("br"),e._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),e._v(" "),r("p",[e._v("Rust 对可靠性的执着也延伸到了错误处理。错误对于软件来说是不可避免的,所以 Rust 有很多特性来处理出现错误的情况。在很多情况下,Rust 要求你承认出错的可能性并在编译代码之前就采取行动。这些要求使得程序更为健壮,它们确保了你会在将代码部署到生产环境之前就发现错误并正确地处理它们!")]),e._v(" "),e._m(1),e._v(" "),e._m(2)])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"错误处理"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#错误处理","aria-hidden":"true"}},[this._v("#")]),this._v(" 错误处理")])},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("p",[e._v("Rust 将错误组合成两个主要类别:"),r("strong",[e._v("可恢复错误")]),e._v("("),r("em",[e._v("recoverable")]),e._v(")和 "),r("strong",[e._v("不可恢复错误")]),e._v("("),r("em",[e._v("unrecoverable")]),e._v(")。可恢复错误通常代表向用户报告错误和重试操作是合理的情况,比如未找到文件。不可恢复错误通常是 bug 的同义词,比如尝试访问超过数组结尾的位置。")])},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("p",[e._v("大部分语言并不区分这两类错误,并采用类似异常这样方式统一处理他们。Rust 并没有异常。相反,对于可恢复错误有 "),r("code",[e._v("Result")]),e._v(" 值,以及 "),r("code",[e._v("panic!")]),e._v(",它在遇到不可恢复错误时停止程序执行。这一章会首先介绍 "),r("code",[e._v("panic!")]),e._v(" 调用,接着会讲到如何返回 "),r("code",[e._v("Result")]),e._v("。此外,我们将探讨决定是尝试从错误中恢复还是停止执行时的注意事项。")])}],!1,null,null,null);_.options.__file="ch09-00-error-handling.md";t.default=_.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/48.50b8e29c.js b/src/.vuepress/dist/assets/js/48.50b8e29c.js
new file mode 100644
index 0000000..14e456d
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/48.50b8e29c.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[48],{222:function(t,e,c){"use strict";c.r(e);var n=c(0),a=Object(n.a)({},function(){var t=this,e=t.$createElement,c=t._self._c||e;return c("div",{staticClass:"content"},[t._m(0),t._v(" "),c("blockquote",[c("p",[c("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch09-01-unrecoverable-errors-with-panic.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch09-01-unrecoverable-errors-with-panic.md"),c("OutboundLink")],1),t._v(" "),c("br"),t._v("\ncommit d073ece693e880b69412e645e4eabe99e74e7590")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),c("p",[t._v("运行程序将会出现类似这样的输出:")]),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),c("p",[t._v("为了使程序远离这类漏洞,如果尝试读取一个索引不存在的元素,Rust 会停止执行并拒绝继续。尝试运行上面的程序会出现如下:")]),t._v(" "),t._m(16),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"panic-与不可恢复的错误"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#panic-与不可恢复的错误","aria-hidden":"true"}},[this._v("#")]),this._v(" "),e("code",[this._v("panic!")]),this._v(" 与不可恢复的错误")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("突然有一天,代码出问题了,而你对此束手无策。对于这种情况,Rust 有 "),e("code",[this._v("panic!")]),this._v("宏。当执行这个宏时,程序会打印出一个错误信息,展开并清理栈数据,然后接着退出。出现这种情况的场景通常是检测到一些类型的 bug 而且程序员并不清楚该如何处理它。")])},function(){var t=this,e=t.$createElement,c=t._self._c||e;return c("blockquote",[c("h3",{attrs:{id:"对应-panic-时的栈展开或终止"}},[c("a",{staticClass:"header-anchor",attrs:{href:"#对应-panic-时的栈展开或终止","aria-hidden":"true"}},[t._v("#")]),t._v(" 对应 panic 时的栈展开或终止")]),t._v(" "),c("p",[t._v("当出现 panic 时,程序默认会开始 "),c("strong",[t._v("展开")]),t._v("("),c("em",[t._v("unwinding")]),t._v("),这意味着 Rust 会回溯栈并清理它遇到的每一个函数的数据,不过这个回溯并清理的过程有很多工作。另一种选择是直接 "),c("strong",[t._v("终止")]),t._v("("),c("em",[t._v("abort")]),t._v("),这会不清理数据就退出程序。那么程序所使用的内存需要由操作系统来清理。如果你需要项目的最终二进制文件越小越好,panic 时通过在 "),c("em",[t._v("Cargo.toml")]),t._v(" 的 "),c("code",[t._v("[profile]")]),t._v(" 部分增加 "),c("code",[t._v("panic = 'abort'")]),t._v(",可以由展开切换为终止。例如,如果你想要在release模式中 panic 时直接终止:")]),t._v(" "),c("div",{staticClass:"language-toml extra-class"},[c("pre",{pre:!0,attrs:{class:"language-text"}},[c("code",[t._v("[profile.release]\npanic = 'abort'\n")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("让我们在一个简单的程序中调用 "),e("code",[this._v("panic!")]),this._v(":")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,should_panic,panics extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('fn main() {\n panic!("crash and burn");\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo run\n Compiling panic v0.1.0 (file:///projects/panic)\n Finished dev [unoptimized + debuginfo] target(s) in 0.25 secs\n Running `target/debug/panic`\nthread 'main' panicked at 'crash and burn', src/main.rs:2:4\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("最后两行包含 "),e("code",[this._v("panic!")]),this._v(" 调用造成的错误信息。第一行显示了 panic 提供的信息并指明了源码中 panic 出现的位置:"),e("em",[this._v("src/main.rs:2:4")]),this._v(" 表明这是 "),e("em",[this._v("src/main.rs")]),this._v(" 文件的第二行第四个字符。")])},function(){var t=this,e=t.$createElement,c=t._self._c||e;return c("p",[t._v("在这个例子中,被指明的那一行是我们代码的一部分,而且查看这一行的话就会发现 "),c("code",[t._v("panic!")]),t._v(" 宏的调用。在其他情况下,"),c("code",[t._v("panic!")]),t._v(" 可能会出现在我们的代码调用的代码中。错误信息报告的文件名和行号可能指向别人代码中的 "),c("code",[t._v("panic!")]),t._v(" 宏调用,而不是我们代码中最终导致 "),c("code",[t._v("panic!")]),t._v(" 的那一行。可以使用 "),c("code",[t._v("panic!")]),t._v(" 被调用的函数的 backtrace 来寻找(我们代码中出问题的地方)。下面我们会详细介绍 backtrace 是什么。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"使用-panic-的-backtrace"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#使用-panic-的-backtrace","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),e("code",[this._v("panic!")]),this._v(" 的 backtrace")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("让我们来看看另一个因为我们代码中的 bug 引起的别的库中 "),e("code",[this._v("panic!")]),this._v(" 的例子,而不是直接的宏调用。示例 9-1 有一些尝试通过索引访问 vector 中元素的例子:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,should_panic,panics extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("fn main() {\n let v = vec![1, 2, 3];\n\n v[99];\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 9-1:尝试访问超越 vector 结尾的元素,这会造成 "),e("code",[this._v("panic!")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("这里尝试访问 vector 的第一百个元素(这里的索引是 99 因为索引从 0 开始),不过它只有三个元素。这种情况下 Rust 会 panic。"),e("code",[this._v("[]")]),this._v(" 应当返回一个元素,不过如果传递了一个无效索引,就没有可供 Rust 返回的正确的元素。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("这种情况下其他像 C 这样语言会尝试直接提供所要求的值,即便这可能不是你期望的:你会得到任何对应 vector 中这个元素的内存位置的值,甚至是这些内存并不属于 vector 的情况。这被称为 "),e("strong",[this._v("缓冲区溢出")]),this._v("("),e("em",[this._v("buffer overread")]),this._v("),并可能会导致安全漏洞,比如攻击者可以像这样操作索引来读取储存在数组后面不被允许的数据。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo run\n Compiling panic v0.1.0 (file:///projects/panic)\n Finished dev [unoptimized + debuginfo] target(s) in 0.27 secs\n Running `target/debug/panic`\nthread 'main' panicked at 'index out of bounds: the len is 3 but the index is\n99', /checkout/src/liballoc/vec.rs:1555:10\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n")])])])},function(){var t=this,e=t.$createElement,c=t._self._c||e;return c("p",[t._v("这指向了一个不是我们编写的文件,"),c("em",[t._v("vec.rs")]),t._v("。这是标准库中 "),c("code",[t._v("Vec")]),t._v(" 的实现。这是当对 vector "),c("code",[t._v("v")]),t._v(" 使用 "),c("code",[t._v("[]")]),t._v(" 时 "),c("em",[t._v("vec.rs")]),t._v(" 中会执行的代码,也是真正出现 "),c("code",[t._v("panic!")]),t._v(" 的地方。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("接下来的几行提醒我们可以设置 "),e("code",[this._v("RUST_BACKTRACE")]),this._v(" 环境变量来得到一个 backtrace "),e("em",[this._v("backtrace")]),this._v(" 是一个执行到目前位置所有被调用的函数的列表。Rust 的 backtrace 跟其他语言中的一样:阅读 backtrace 的关键是从头开始读直到发现你编写的文件。这就是问题的发源地。这一行往上是你的代码调用的代码;往下则是调用你的代码的代码。这些行可能包含核心 Rust 代码,标准库代码或用到的 crate 代码。让我们将 "),e("code",[this._v("RUST_BACKTRACE")]),this._v(" 环境变量设置为任何不是 0 的值来获取 backtrace 看看。示例 9-2 展示了与你看到类似的输出:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ RUST_BACKTRACE=1 cargo run\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/panic`\nthread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', /checkout/src/liballoc/vec.rs:1555:10\nstack backtrace:\n 0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace\n at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49\n 1: std::sys_common::backtrace::_print\n at /checkout/src/libstd/sys_common/backtrace.rs:71\n 2: std::panicking::default_hook::{{closure}}\n at /checkout/src/libstd/sys_common/backtrace.rs:60\n at /checkout/src/libstd/panicking.rs:381\n 3: std::panicking::default_hook\n at /checkout/src/libstd/panicking.rs:397\n 4: std::panicking::rust_panic_with_hook\n at /checkout/src/libstd/panicking.rs:611\n 5: std::panicking::begin_panic\n at /checkout/src/libstd/panicking.rs:572\n 6: std::panicking::begin_panic_fmt\n at /checkout/src/libstd/panicking.rs:522\n 7: rust_begin_unwind\n at /checkout/src/libstd/panicking.rs:498\n 8: core::panicking::panic_fmt\n at /checkout/src/libcore/panicking.rs:71\n 9: core::panicking::panic_bounds_check\n at /checkout/src/libcore/panicking.rs:58\n 10: as core::ops::index::Index>::index\n at /checkout/src/liballoc/vec.rs:1555\n 11: panic::main\n at src/main.rs:4\n 12: __rust_maybe_catch_panic\n at /checkout/src/libpanic_unwind/lib.rs:99\n 13: std::rt::lang_start\n at /checkout/src/libstd/panicking.rs:459\n at /checkout/src/libstd/panic.rs:361\n at /checkout/src/libstd/rt.rs:61\n 14: main\n 15: __libc_start_main\n 16: \n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 9-2:当设置 "),e("code",[this._v("RUST_BACKTRACE")]),this._v(" 环境变量时 "),e("code",[this._v("panic!")]),this._v(" 调用所生成的 backtrace 信息")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("这里有大量的输出!你实际看到的输出可能因不同的操作系统和 Rust 版本而有所不同。为了获取带有这些信息的 backtrace,必须启用 debug 标识。当不使用 "),e("code",[this._v("--release")]),this._v(" 参数运行 cargo build 或 cargo run 时 debug 标识会默认启用,就像这里一样。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("示例 9-2 的输出中,backtrace 的 11 行指向了我们项目中造成问题的行:"),e("em",[this._v("src/main.rs")]),this._v(" 的第 4 行。如果你不希望程序 panic,第一个提到我们编写的代码行的位置是你应该开始调查的,以便查明是什么值如何在这个地方引起了 panic。在示例 9-1 中,我们故意编写会 panic 的代码来演示如何使用 backtrace,修复这个 panic 的方法就是不要尝试在一个只包含三个项的 vector 中请求索引是 100 的元素。当将来你的代码出现了 panic,你需要搞清楚在这特定的场景下代码中执行了什么操作和什么值导致了 panic,以及应当如何处理才能避免这个问题。")])},function(){var t=this,e=t.$createElement,c=t._self._c||e;return c("p",[t._v("本章后面的小节 “panic! 还是不 panic!”会再次回到 "),c("code",[t._v("panic!")]),t._v(" 会回到 "),c("code",[t._v("panic!")]),t._v(" 并讲解何时应该何时不应该使用 "),c("code",[t._v("panic!")]),t._v(" 来处理错误情况。接下来,我们来看看如何使用 "),c("code",[t._v("Result")]),t._v(" 来从错误中恢复。")])}],!1,null,null,null);a.options.__file="ch09-01-unrecoverable-errors-with-panic.md";e.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/49.385dc110.js b/src/.vuepress/dist/assets/js/49.385dc110.js
new file mode 100644
index 0000000..c49717b
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/49.385dc110.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[49],{276:function(t,s,n){"use strict";n.r(s);var e=n(0),a=Object(e.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch09-02-recoverable-errors-with-result.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch09-02-recoverable-errors-with-result.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit db53e2e3cdf77beac853df6f29db4b3b86ea598c")])]),t._v(" "),n("p",[t._v("大部分错误并没有严重到需要程序完全停止执行。有时,一个函数会因为一个容易理解并做出反应的原因失败。例如,如果尝试打开一个文件不过由于文件并不存在而失败,此时我们可能想要创建这个文件而不是终止进程。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),n("p",[t._v("如何知道 "),n("code",[t._v("File::open")]),t._v(" 返回一个 "),n("code",[t._v("Result")]),t._v(" 呢?我们可以查看 "),n("a",{attrs:{href:"https://doc.rust-lang.org/std/index.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("标准库 API 文档"),n("OutboundLink")],1),t._v(",或者可以直接问编译器!如果给 "),n("code",[t._v("f")]),t._v(" 某个我们知道 "),n("strong",[t._v("不是")]),t._v(" 函数返回值类型的类型注解,接着尝试编译代码,编译器会告诉我们类型不匹配。然后错误信息会告诉我们 "),n("code",[t._v("f")]),t._v(" 的类型 "),n("strong",[t._v("应该")]),t._v(" 是什么。让我们试试!我们知道 "),n("code",[t._v("File::open")]),t._v(" 的返回值不是 "),n("code",[t._v("u32")]),t._v(" 类型的,所以将 "),n("code",[t._v("let f")]),t._v(" 语句改为如下:")]),t._v(" "),t._m(8),n("p",[t._v("现在尝试编译会给出如下输出:")]),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),n("p",[t._v("输出一如既往告诉了我们到底出了什么错。")]),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._m(35),t._v(" "),t._m(36),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),t._m(40),t._v(" "),t._m(41),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),n("p",[t._v("例如,示例 9-6 展示了一个从文件中读取用户名的函数。如果文件不存在或不能读取,这个函数会将这些错误返回给调用它的代码:")]),t._v(" "),t._m(45),t._v(" "),t._m(46),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._v(" "),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),t._v(" "),t._m(56),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._v(" "),t._m(60),t._v(" "),t._m(61),t._v(" "),t._m(62),t._v(" "),t._m(63),t._m(64),t._v(" "),t._m(65),t._v(" "),n("p",[t._v("说到编写这个函数的不同方法,甚至还有一个更短的写法:")]),t._v(" "),t._m(66),t._v(" "),t._m(67),t._m(68),t._v(" "),t._m(69),t._v(" "),t._m(70),t._v(" "),t._m(71),t._v(" "),t._m(72),t._v(" "),t._m(73),n("p",[t._v("当编译这些代码,会得到如下错误信息:")]),t._v(" "),t._m(74),t._m(75),t._v(" "),t._m(76),t._v(" "),t._m(77),t._m(78),t._v(" "),t._m(79)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"result-与可恢复的错误"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#result-与可恢复的错误","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("Result")]),this._v(" 与可恢复的错误")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("回忆一下第二章 “使用 "),n("code",[t._v("Result")]),t._v(" 类型来处理潜在的错误” 部分中的那个 "),n("code",[t._v("Result")]),t._v(" 枚举,它定义有如下两个成员,"),n("code",[t._v("Ok")]),t._v(" 和 "),n("code",[t._v("Err")]),t._v(":")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" E"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("E"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("T")]),t._v(" 和 "),n("code",[t._v("E")]),t._v(" 是泛型类型参数;第十章会详细介绍泛型。现在你需要知道的就是 "),n("code",[t._v("T")]),t._v(" 代表成功时返回的 "),n("code",[t._v("Ok")]),t._v(" 成员中的数据的类型,而 "),n("code",[t._v("E")]),t._v(" 代表失败时返回的 "),n("code",[t._v("Err")]),t._v(" 成员中的错误的类型。因为 "),n("code",[t._v("Result")]),t._v(" 有这些泛型类型参数,我们可以将 "),n("code",[t._v("Result")]),t._v(" 类型和标准库中为其定义的函数用于很多不同的场景,这些情况中需要返回的成功值和失败值可能会各不相同。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们调用一个返回 "),s("code",[this._v("Result")]),this._v(" 的函数,因为它可能会失败:如示例 9-3 所示打开一个文件:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("File"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" f "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" File"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("open")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello.txt"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 9-3:打开文件")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let f: u32 = File::open("hello.txt");\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0308]: mismatched types\n --\x3e src/main.rs:4:18\n |\n4 | let f: u32 = File::open("hello.txt");\n | ^^^^^^^^^^^^^^^^^^^^^^^ expected u32, found enum\n`std::result::Result`\n |\n = note: expected type `u32`\n found type `std::result::Result`\n')])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这就告诉我们了 "),n("code",[t._v("File::open")]),t._v(" 函数的返回值类型是 "),n("code",[t._v("Result")]),t._v("。这里泛型参数 "),n("code",[t._v("T")]),t._v(" 放入了成功值的类型 "),n("code",[t._v("std::fs::File")]),t._v(",它是一个文件句柄。"),n("code",[t._v("E")]),t._v(" 被用在失败值上时 "),n("code",[t._v("E")]),t._v(" 的类型是 "),n("code",[t._v("std::io::Error")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个返回值类型说明 "),s("code",[this._v("File::open")]),this._v(" 调用可能会成功并返回一个可以进行读写的文件句柄。这个函数也可能会失败:例如,文件可能并不存在,或者可能没有访问文件的权限。"),s("code",[this._v("File::open")]),this._v(" 需要一个方式告诉我们是成功还是失败,并同时提供给我们文件句柄或错误信息。而这些信息正是 "),s("code",[this._v("Result")]),this._v(" 枚举可以提供的。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当 "),n("code",[t._v("File::open")]),t._v(" 成功的情况下,变量 "),n("code",[t._v("f")]),t._v(" 的值将会是一个包含文件句柄的 "),n("code",[t._v("Ok")]),t._v(" 实例。在失败的情况下,"),n("code",[t._v("f")]),t._v(" 的值会是一个包含更多关于出现了何种错误信息的 "),n("code",[t._v("Err")]),t._v(" 实例。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们需要在示例 9-3 的代码中增加根据 "),s("code",[this._v("File::open")]),this._v(" 返回值进行不同处理的逻辑。示例 9-4 展示了一个使用基本工具处理 "),s("code",[this._v("Result")]),this._v(" 的例子:第六章学习过的 "),s("code",[this._v("match")]),this._v(" 表达式。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,should_panic extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('use std::fs::File;\n\nfn main() {\n let f = File::open("hello.txt");\n\n let f = match f {\n Ok(file) => file,\n Err(error) => {\n panic!("There was a problem opening the file: {:?}", error)\n },\n };\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 9-4:使用 "),s("code",[this._v("match")]),this._v(" 表达式处理可能的 "),s("code",[this._v("Result")]),this._v(" 成员")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意与 "),n("code",[t._v("Option")]),t._v(" 枚举一样,"),n("code",[t._v("Result")]),t._v(" 枚举和其成员也被导入到了 prelude 中,所以就不需要在 "),n("code",[t._v("match")]),t._v(" 分支中的 "),n("code",[t._v("Ok")]),t._v(" 和 "),n("code",[t._v("Err")]),t._v(" 之前指定 "),n("code",[t._v("Result::")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里我们告诉 Rust 当结果是 "),n("code",[t._v("Ok")]),t._v(" 时,返回 "),n("code",[t._v("Ok")]),t._v(" 成员中的 "),n("code",[t._v("file")]),t._v(" 值,然后将这个文件句柄赋值给变量 "),n("code",[t._v("f")]),t._v("。"),n("code",[t._v("match")]),t._v(" 之后,我们可以利用这个文件句柄来进行读写。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("match")]),t._v(" 的另一个分支处理从 "),n("code",[t._v("File::open")]),t._v(" 得到 "),n("code",[t._v("Err")]),t._v(" 值的情况。在这种情况下,我们选择调用 "),n("code",[t._v("panic!")]),t._v(" 宏。如果当前目录没有一个叫做 "),n("em",[t._v("hello.txt")]),t._v(" 的文件,当运行这段代码时会看到如下来自 "),n("code",[t._v("panic!")]),t._v(" 宏的输出:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("thread 'main' panicked at 'There was a problem opening the file: Error { repr:\nOs { code: 2, message: \"No such file or directory\" } }', src/main.rs:9:12\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"匹配不同的错误"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#匹配不同的错误","aria-hidden":"true"}},[this._v("#")]),this._v(" 匹配不同的错误")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 9-4 中的代码不管 "),n("code",[t._v("File::open")]),t._v(" 是因为什么原因失败都会 "),n("code",[t._v("panic!")]),t._v("。我们真正希望的是对不同的错误原因采取不同的行为:如果 "),n("code",[t._v("File::open")]),t._v("因为文件不存在而失败,我们希望创建这个文件并返回新文件的句柄。如果 "),n("code",[t._v("File::open")]),t._v(" 因为任何其他原因失败,例如没有打开文件的权限,我们仍然希望像示例 9-4 那样 "),n("code",[t._v("panic!")]),t._v("。让我们看看示例 9-5,其中 "),n("code",[t._v("match")]),t._v(" 增加了另一个分支:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('use std::fs::File;\nuse std::io::ErrorKind;\n\nfn main() {\n let f = File::open("hello.txt");\n\n let f = match f {\n Ok(file) => file,\n Err(error) => match error.kind() {\n ErrorKind::NotFound => match File::create("hello.txt") {\n Ok(fc) => fc,\n Err(e) => panic!("Tried to create file but there was a problem: {:?}", e),\n },\n other_error => panic!("There was a problem opening the file: {:?}", other_error),\n },\n };\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 9-5:使用不同的方式处理不同类型的错误")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("File::open")]),t._v(" 返回的 "),n("code",[t._v("Err")]),t._v(" 成员中的值类型 "),n("code",[t._v("io::Error")]),t._v(",它是一个标准库中提供的结构体。这个结构体有一个返回 "),n("code",[t._v("io::ErrorKind")]),t._v(" 值的 "),n("code",[t._v("kind")]),t._v(" 方法可供调用。"),n("code",[t._v("io::ErrorKind")]),t._v(" 是一个标准库提供的枚举,它的成员对应 "),n("code",[t._v("io")]),t._v(" 操作可能导致的不同错误类型。我们感兴趣的成员是 "),n("code",[t._v("ErrorKind::NotFound")]),t._v(",它代表尝试打开的文件并不存在。所以 "),n("code",[t._v("match")]),t._v(" 的 "),n("code",[t._v("f")]),t._v(" 匹配,不过对于 "),n("code",[t._v("error.kind()")]),t._v(" 还有一个内部 "),n("code",[t._v("match")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们希望在匹配守卫中检查的条件是 "),n("code",[t._v("error.kind()")]),t._v(" 的返回值是 "),n("code",[t._v("ErrorKind")]),t._v("的 "),n("code",[t._v("NotFound")]),t._v(" 成员。如果是,则尝试通过 "),n("code",[t._v("File::create")]),t._v(" 创建文件。然而因为 "),n("code",[t._v("File::create")]),t._v(" 也可能会失败,还需要增加一个内部 "),n("code",[t._v("match")]),t._v(" 语句。当文件不能被打开,会打印出一个不同的错误信息。外部 "),n("code",[t._v("match")]),t._v(" 的最后一个分支保持不变这样对任何除了文件不存在的错误会使程序 panic。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里有好多 "),n("code",[t._v("match")]),t._v("!"),n("code",[t._v("match")]),t._v(" 确实很强大,不过也非常的基础。第十三章我们会介绍闭包(closure)。"),n("code",[t._v("Result")]),t._v(" 有很多接受闭包的方法,并采用 "),n("code",[t._v("match")]),t._v(" 表达式实现。一个更老练的 Rustacean 可能会这么写:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('use std::fs::File;\nuse std::io::ErrorKind;\n\nfn main() {\n let f = File::open("hello.txt").map_err(|error| {\n if error.kind() == ErrorKind::NotFound {\n File::create("hello.txt").unwrap_or_else(|error| {\n panic!("Tried to create file but there was a problem: {:?}", error);\n })\n } else {\n panic!("There was a problem opening the file: {:?}", error);\n }\n });\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在阅读完第十三章后再回到这个例子,并查看标准库文档 "),s("code",[this._v("map_err")]),this._v(" 和 "),s("code",[this._v("unwrap_or_else")]),this._v(" 方法都做了什么操作。还有很多这类方法可以消除大量处理错误时嵌套的 "),s("code",[this._v("match")]),this._v(" 表达式。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"失败时-panic-的简写:unwrap-和-expect"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#失败时-panic-的简写:unwrap-和-expect","aria-hidden":"true"}},[this._v("#")]),this._v(" 失败时 panic 的简写:"),s("code",[this._v("unwrap")]),this._v(" 和 "),s("code",[this._v("expect")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("match")]),t._v(" 能够胜任它的工作,不过它可能有点冗长并且不总是能很好的表明其意图。"),n("code",[t._v("Result")]),t._v(" 类型定义了很多辅助方法来处理各种情况。其中之一叫做 "),n("code",[t._v("unwrap")]),t._v(",它的实现就类似于示例 9-4 中的 "),n("code",[t._v("match")]),t._v(" 语句。如果 "),n("code",[t._v("Result")]),t._v(" 值是成员 "),n("code",[t._v("Ok")]),t._v(","),n("code",[t._v("unwrap")]),t._v(" 会返回 "),n("code",[t._v("Ok")]),t._v(" 中的值。如果 "),n("code",[t._v("Result")]),t._v(" 是成员 "),n("code",[t._v("Err")]),t._v(","),n("code",[t._v("unwrap")]),t._v(" 会为我们调用 "),n("code",[t._v("panic!")]),t._v("。这里是一个实践 "),n("code",[t._v("unwrap")]),t._v(" 的例子:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,should_panic extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('use std::fs::File;\n\nfn main() {\n let f = File::open("hello.txt").unwrap();\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果调用这段代码时不存在 "),s("em",[this._v("hello.txt")]),this._v(" 文件,我们将会看到一个 "),s("code",[this._v("unwrap")]),this._v(" 调用 "),s("code",[this._v("panic!")]),this._v(" 时提供的错误信息:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error {\nrepr: Os { code: 2, message: \"No such file or directory\" } }',\nsrc/libcore/result.rs:906:4\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("还有另一个类似于 "),n("code",[t._v("unwrap")]),t._v(" 的方法它还允许我们选择 "),n("code",[t._v("panic!")]),t._v(" 的错误信息:"),n("code",[t._v("expect")]),t._v("。使用 "),n("code",[t._v("expect")]),t._v(" 而不是 "),n("code",[t._v("unwrap")]),t._v(" 并提供一个好的错误信息可以表明你的意图并更易于追踪 panic 的根源。"),n("code",[t._v("expect")]),t._v(" 的语法看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,should_panic extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('use std::fs::File;\n\nfn main() {\n let f = File::open("hello.txt").expect("Failed to open hello.txt");\n}\n')])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("expect")]),t._v(" 与 "),n("code",[t._v("unwrap")]),t._v(" 的使用方式一样:返回文件句柄或调用 "),n("code",[t._v("panic!")]),t._v(" 宏。"),n("code",[t._v("expect")]),t._v(" 用来调用 "),n("code",[t._v("panic!")]),t._v(" 的错误信息将会作为参数传递给 "),n("code",[t._v("expect")]),t._v(" ,而不像"),n("code",[t._v("unwrap")]),t._v(" 那样使用默认的 "),n("code",[t._v("panic!")]),t._v(" 信息。它看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:\n2, message: \"No such file or directory\" } }', src/libcore/result.rs:906:4\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("因为这个错误信息以我们指定的文本开始,"),n("code",[t._v("Failed to open hello.txt")]),t._v(",将会更容易找到代码中的错误信息来自何处。如果在多处使用 "),n("code",[t._v("unwrap")]),t._v(",则需要花更多的时间来分析到底是哪一个 "),n("code",[t._v("unwrap")]),t._v(" 造成了 panic,因为所有的 "),n("code",[t._v("unwrap")]),t._v(" 调用都打印相同的信息。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"传播错误"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#传播错误","aria-hidden":"true"}},[this._v("#")]),this._v(" 传播错误")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当编写一个其实现会调用一些可能会失败的操作的函数时,除了在这个函数中处理错误外,还可以选择让调用者知道这个错误并决定该如何处理。这被称为 "),s("strong",[this._v("传播")]),this._v("("),s("em",[this._v("propagating")]),this._v(")错误,这样能更好的控制代码调用,因为比起你代码所拥有的上下文,调用者可能拥有更多信息或逻辑来决定应该如何处理错误。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("Filename: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Read"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("File"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("read_username_from_file")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Error"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" f "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" File"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("open")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello.txt"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" f "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" f "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("file"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" file"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" f"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read_to_string")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 9-6:一个函数使用 "),s("code",[this._v("match")]),this._v(" 将错误返回给代码调用者")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("首先让我们看看函数的返回值:"),n("code",[t._v("Result")]),t._v("。这意味着函数返回一个 "),n("code",[t._v("Result")]),t._v(" 类型的值,其中泛型参数 "),n("code",[t._v("T")]),t._v(" 的具体类型是 "),n("code",[t._v("String")]),t._v(",而 "),n("code",[t._v("E")]),t._v(" 的具体类型是 "),n("code",[t._v("io::Error")]),t._v("。如果这个函数没有出任何错误成功返回,函数的调用者会收到一个包含 "),n("code",[t._v("String")]),t._v(" 的 "),n("code",[t._v("Ok")]),t._v(" 值 —— 函数从文件中读取到的用户名。如果函数遇到任何错误,函数的调用者会收到一个 "),n("code",[t._v("Err")]),t._v(" 值,它储存了一个包含更多这个问题相关信息的 "),n("code",[t._v("io::Error")]),t._v(" 实例。这里选择 "),n("code",[t._v("io::Error")]),t._v(" 作为函数的返回值是因为它正好是函数体中那两个可能会失败的操作的错误返回值:"),n("code",[t._v("File::open")]),t._v(" 函数和 "),n("code",[t._v("read_to_string")]),t._v(" 方法。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("函数体以 "),n("code",[t._v("File::open")]),t._v(" 函数开头。接着使用 "),n("code",[t._v("match")]),t._v(" 处理返回值 "),n("code",[t._v("Result")]),t._v(",类似于示例 9-4 中的 "),n("code",[t._v("match")]),t._v(",唯一的区别是当 "),n("code",[t._v("Err")]),t._v(" 时不再调用 "),n("code",[t._v("panic!")]),t._v(",而是提早返回并将 "),n("code",[t._v("File::open")]),t._v(" 返回的错误值作为函数的错误返回值传递给调用者。如果 "),n("code",[t._v("File::open")]),t._v(" 成功了,我们将文件句柄储存在变量 "),n("code",[t._v("f")]),t._v(" 中并继续。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("接着我们在变量 "),n("code",[t._v("s")]),t._v(" 中创建了一个新 "),n("code",[t._v("String")]),t._v(" 并调用文件句柄 "),n("code",[t._v("f")]),t._v(" 的 "),n("code",[t._v("read_to_string")]),t._v(" 方法来将文件的内容读取到 "),n("code",[t._v("s")]),t._v(" 中。"),n("code",[t._v("read_to_string")]),t._v(" 方法也返回一个 "),n("code",[t._v("Result")]),t._v(" 因为它也可能会失败:哪怕是 "),n("code",[t._v("File::open")]),t._v(" 已经成功了。所以我们需要另一个 "),n("code",[t._v("match")]),t._v(" 来处理这个 "),n("code",[t._v("Result")]),t._v(":如果 "),n("code",[t._v("read_to_string")]),t._v(" 成功了,那么这个函数就成功了,并返回文件中的用户名,它现在位于被封装进 "),n("code",[t._v("Ok")]),t._v(" 的 "),n("code",[t._v("s")]),t._v(" 中。如果"),n("code",[t._v("read_to_string")]),t._v(" 失败了,则像之前处理 "),n("code",[t._v("File::open")]),t._v(" 的返回值的 "),n("code",[t._v("match")]),t._v(" 那样返回错误值。不过并不需要显式的调用 "),n("code",[t._v("return")]),t._v(",因为这是函数的最后一个表达式。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("调用这个函数的代码最终会得到一个包含用户名的 "),n("code",[t._v("Ok")]),t._v(" 值,或者一个包含 "),n("code",[t._v("io::Error")]),t._v(" 的 "),n("code",[t._v("Err")]),t._v(" 值。我们无从得知调用者会如何处理这些值。例如,如果他们得到了一个 "),n("code",[t._v("Err")]),t._v(" 值,他们可能会选择 "),n("code",[t._v("panic!")]),t._v(" 并使程序崩溃、使用一个默认的用户名或者从文件之外的地方寻找用户名。我们没有足够的信息知晓调用者具体会如何尝试,所以将所有的成功或失败信息向上传播,让他们选择合适的处理方法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这种传播错误的模式在 Rust 是如此的常见,以至于有一个更简便的专用语法:"),s("code",[this._v("?")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"传播错误的简写:"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#传播错误的简写:","aria-hidden":"true"}},[this._v("#")]),this._v(" 传播错误的简写:"),s("code",[this._v("?")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 9-7 展示了一个 "),s("code",[this._v("read_username_from_file")]),this._v(" 的实现,它实现了与示例 9-6 中的代码相同的功能,不过这个实现使用了问号运算符:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Read"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("File"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("read_username_from_file")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Error"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" f "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" File"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("open")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello.txt"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("?"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n f"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read_to_string")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("?"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 9-6:一个使用 "),s("code",[this._v("?")]),this._v(" 向调用者返回错误的函数")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Result")]),t._v(" 值之后的 "),n("code",[t._v("?")]),t._v(" 被定义为与示例 9-6 中定义的处理 "),n("code",[t._v("Result")]),t._v(" 值的 "),n("code",[t._v("match")]),t._v(" 表达式有着完全相同的工作方式。如果 "),n("code",[t._v("Result")]),t._v(" 的值是 "),n("code",[t._v("Ok")]),t._v(",这个表达式将会返回 "),n("code",[t._v("Ok")]),t._v(" 中的值而程序将继续执行。如果值是 "),n("code",[t._v("Err")]),t._v(","),n("code",[t._v("Err")]),t._v(" 中的值将作为整个函数的返回值,就好像使用了 "),n("code",[t._v("return")]),t._v(" 关键字一样,这样错误值就被传播给了调用者。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 9-6 中的 "),n("code",[t._v("match")]),t._v(" 表达式与问号运算符所做的有一点不同:"),n("code",[t._v("?")]),t._v(" 所使用的错误值被传递给了 "),n("code",[t._v("from")]),t._v(" 函数,它定义于标准库的 "),n("code",[t._v("From")]),t._v(" trait 中,其用来将错误从一种类型转换为另一种类型。当 "),n("code",[t._v("?")]),t._v(" 调用 "),n("code",[t._v("from")]),t._v(" 函数时,收到的错误类型被转换为定义为当前函数返回的错误类型。这在当一个函数返回一个错误类型来代表所有可能失败的方式时很有用,即使其可能会因很多种原因失败。只要每一个错误类型都实现了 "),n("code",[t._v("from")]),t._v(" 函数来定义如将其转换为返回的错误类型,"),n("code",[t._v("?")]),t._v(" 会自动处理这些转换。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在示例 9-7 的上下文中,"),n("code",[t._v("File::open")]),t._v(" 调用结尾的 "),n("code",[t._v("?")]),t._v(" 将会把 "),n("code",[t._v("Ok")]),t._v(" 中的值返回给变量 "),n("code",[t._v("f")]),t._v("。如果出现了错误,"),n("code",[t._v("?")]),t._v(" 会提早返回整个函数并将一些 "),n("code",[t._v("Err")]),t._v(" 值传播给调用者。同理也适用于 "),n("code",[t._v("read_to_string")]),t._v(" 调用结尾的 "),n("code",[t._v("?")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("?")]),this._v(" 消除了大量样板代码并使得函数的实现更简单。我们甚至可以在 "),s("code",[this._v("?")]),this._v(" 之后直接使用链式方法调用来进一步缩短代码,如示例 9-8 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Read"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("File"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("read_username_from_file")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Error"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n File"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("open")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello.txt"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("?"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("read_to_string")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("?"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 9-8:问号运算符之后的链式方法调用")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 "),n("code",[t._v("s")]),t._v(" 中创建新的 "),n("code",[t._v("String")]),t._v(" 被放到了函数开头;这一部分没有变化。我们对 "),n("code",[t._v('File::open("hello.txt")?')]),t._v(" 的结果直接链式调用了 "),n("code",[t._v("read_to_string")]),t._v(",而不再创建变量 "),n("code",[t._v("f")]),t._v("。仍然需要 "),n("code",[t._v("read_to_string")]),t._v(" 调用结尾的 "),n("code",[t._v("?")]),t._v(",而且当 "),n("code",[t._v("File::open")]),t._v(" 和 "),n("code",[t._v("read_to_string")]),t._v(" 都成功没有失败时返回包含用户名 "),n("code",[t._v("s")]),t._v(" 的 "),n("code",[t._v("Ok")]),t._v(" 值。其功能再一次与示例 9-6 和示例 9-7 保持一致,不过这是一个与众不同且更符合工程学的写法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("read_username_from_file")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Error"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("read_to_string")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"hello.txt"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 9-9: 使用 "),s("code",[this._v("fs::read_to_string")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("将文件读取到一个字符串是相当常见的操作,所以 Rust 提供了名为 "),s("code",[this._v("fs::read_to_string")]),this._v(" 的函数,它会打开文件、新建一个 "),s("code",[this._v("String")]),this._v("、读取文件的内容,并将内容放入 "),s("code",[this._v("String")]),this._v(",接着返回它。当然,这样做就没有展示所有这些错误处理的机会了,所以我们最初就选择了艰苦的道路。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"只能被用于返回-result-的函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#只能被用于返回-result-的函数","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("?")]),this._v(" 只能被用于返回 "),s("code",[this._v("Result")]),this._v(" 的函数")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("?")]),t._v(" 只能被用于返回值类型为 "),n("code",[t._v("Result")]),t._v(" 的函数,因为他被定义为与示例 9-6 中的 "),n("code",[t._v("match")]),t._v(" 表达式有着完全相同的工作方式。"),n("code",[t._v("match")]),t._v(" 的 "),n("code",[t._v("return Err(e)")]),t._v(" 部分要求返回值类型是 "),n("code",[t._v("Result")]),t._v(",所以函数的返回值必须是 "),n("code",[t._v("Result")]),t._v(" 才能与这个 "),n("code",[t._v("return")]),t._v(" 相兼容。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们看看在 "),s("code",[this._v("main")]),this._v(" 函数中使用 "),s("code",[this._v("?")]),this._v(" 会发生什么,如果你还记得的话其返回值类型是"),s("code",[this._v("()")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('use std::fs::File;\n\nfn main() {\n let f = File::open("hello.txt")?;\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)\n --\x3e src/main.rs:4:13\n |\n4 | let f = File::open("hello.txt")?;\n | ^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`\n |\n = help: the trait `std::ops::Try` is not implemented for `()`\n = note: required by `std::ops::Try::from_error`\n')])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("错误指出只能在返回 "),n("code",[t._v("Result")]),t._v(" 的函数中使用 "),n("code",[t._v("?")]),t._v("。在不返回 "),n("code",[t._v("Result")]),t._v(" 的函数中,当调用其他返回 "),n("code",[t._v("Result")]),t._v(" 的函数时,需要使用 "),n("code",[t._v("match")]),t._v(" 或 "),n("code",[t._v("Result")]),t._v(" 的方法之一来处理,而不能用 "),n("code",[t._v("?")]),t._v(" 将潜在的错误传播给代码调用方。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不过 "),s("code",[this._v("main")]),this._v(" 函数可以返回一个 "),s("code",[this._v("Result")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('use std::error::Error;\nuse std::fs::File;\n\nfn main() -> Result<(), Box> {\n let f = File::open("hello.txt")?;\n\n Ok(())\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("Box")]),this._v(" 被称为 “trait 对象”(“trait object”),第十七章会介绍。目前可以理解 "),s("code",[this._v("Box")]),this._v(" 为 “任何类型的错误”。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在我们讨论过了调用 "),s("code",[this._v("panic!")]),this._v(" 或返回 "),s("code",[this._v("Result")]),this._v(" 的细节,是时候回到他们各自适合哪些场景的话题了。")])}],!1,null,null,null);a.options.__file="ch09-02-recoverable-errors-with-result.md";s.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/5.0204093a.js b/src/.vuepress/dist/assets/js/5.0204093a.js
new file mode 100644
index 0000000..460de8b
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/5.0204093a.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{254:function(t,e,s){"use strict";s.r(e);var i=s(0),n=Object(i.a)({},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[this._m(0),this._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/appendix-00.md",target:"_blank",rel:"noopener noreferrer"}},[this._v("appendix-00.md"),e("OutboundLink")],1),this._v(" "),e("br"),this._v("\ncommit 4f2dc564851dc04b271a2260c834643dfd86c724")])]),this._v(" "),e("p",[this._v("附录部分包含一些在你的Rust之旅中可能用到的参考资料。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"附录"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#附录","aria-hidden":"true"}},[this._v("#")]),this._v(" 附录")])}],!1,null,null,null);n.options.__file="appendix-00.md";e.default=n.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/50.cadce2ee.js b/src/.vuepress/dist/assets/js/50.cadce2ee.js
new file mode 100644
index 0000000..1670f50
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/50.cadce2ee.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[50],{220:function(t,s,e){"use strict";e.r(s);var n=e(0),_=Object(n.a)({},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[t._m(0),t._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch09-03-to-panic-or-not-to-panic.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch09-03-to-panic-or-not-to-panic.md"),e("OutboundLink")],1),t._v(" "),e("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),e("p",[t._v("让我们使用 Rust 类型系统的思想来进一步确保值的有效性,并尝试创建一个自定义类型以进行验证。回忆一下第二章的猜猜看游戏,我们的代码要求用户猜测一个 1 到 100 之间的数字,在将其与秘密数字做比较之前我们从未验证用户的猜测是位于这两个数字之间的,我们只验证它是否为正。在这种情况下,其影响并不是很严重:“Too high” 或 “Too low” 的输出仍然是正确的。但是这是一个很好的引导用户得出有效猜测的辅助,例如当用户猜测一个超出范围的数字或者输入字母时采取不同的行为。")]),t._v(" "),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),e("p",[t._v("然而,这并不是一个理想的解决方案:程序只处理 1 到 100 之间的值是绝对不可取的,而且如果有很多函数都有这样的要求,在每个函数中都有这样的检查将是非常冗余的(并可能潜在的影响性能)。")]),t._v(" "),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"panic-还是不-panic"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#panic-还是不-panic","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("panic!")]),this._v(" 还是不 "),s("code",[this._v("panic!")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("那么,该如何决定何时应该 "),e("code",[t._v("panic!")]),t._v(" 以及何时应该返回 "),e("code",[t._v("Result")]),t._v(" 呢?如果代码 panic,就没有恢复的可能。你可以选择对任何错误场景都调用 "),e("code",[t._v("panic!")]),t._v(",不管是否有可能恢复,不过这样就是你代替调用者决定了这是不可恢复的。选择返回 "),e("code",[t._v("Result")]),t._v(" 值的话,就将选择权交给了调用者,而不是代替他们做出决定。调用者可能会选择以符合他们场景的方式尝试恢复,或者也可能干脆就认为 "),e("code",[t._v("Err")]),t._v(" 是不可恢复的,所以他们也可能会调用 "),e("code",[t._v("panic!")]),t._v(" 并将可恢复的错误变成了不可恢复的错误。因此返回 "),e("code",[t._v("Result")]),t._v(" 是定义可能会失败的函数的一个好的默认选择。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("有一些情况 panic 比返回 "),s("code",[this._v("Result")]),this._v(" 更为合适,不过他们并不常见。让我们讨论一下为何在示例、代码原型和测试中,以及那些人们认为不会失败而编译器不这么看的情况下, panic 是合适的。章节最后会总结一些在库代码中如何决定是否要 panic 的通用指导原则。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"示例、代码原型和测试都非常适合-panic"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#示例、代码原型和测试都非常适合-panic","aria-hidden":"true"}},[this._v("#")]),this._v(" 示例、代码原型和测试都非常适合 panic")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当你编写一个示例来展示一些概念时,在拥有健壮的错误处理代码的同时也会使得例子不那么明确。例如,调用一个类似 "),s("code",[this._v("unwrap")]),this._v(" 这样可能 "),s("code",[this._v("panic!")]),this._v(" 的方法可以被理解为一个你实际希望程序处理错误方式的占位符,它根据其余代码运行方式可能会各不相同。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("类似地,在我们准备好决定如何处理错误之前,"),s("code",[this._v("unwrap")]),this._v("和"),s("code",[this._v("expect")]),this._v("方法在原型设计时非常方便。当我们准备好让程序更加健壮时,它们会在代码中留下清晰的标记。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果方法调用在测试中失败了,我们希望这个测试都失败,即便这个方法并不是需要测试的功能。因为 "),s("code",[this._v("panic!")]),this._v(" 是测试如何被标记为失败的,调用 "),s("code",[this._v("unwrap")]),this._v(" 或 "),s("code",[this._v("expect")]),this._v(" 就是应该发生的事情。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"当你比编译器知道更多的情况"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#当你比编译器知道更多的情况","aria-hidden":"true"}},[this._v("#")]),this._v(" 当你比编译器知道更多的情况")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("当你有一些其他的逻辑来确保 "),e("code",[t._v("Result")]),t._v(" 会是 "),e("code",[t._v("Ok")]),t._v(" 值时,调用 "),e("code",[t._v("unwrap")]),t._v(" 也是合适的,虽然编译器无法理解这种逻辑。你仍然需要处理一个 "),e("code",[t._v("Result")]),t._v(" 值:即使在你的特定情况下逻辑上是不可能的,你所调用的任何操作仍然有可能失败。如果通过人工检查代码来确保永远也不会出现 "),e("code",[t._v("Err")]),t._v(" 值,那么调用 "),e("code",[t._v("unwrap")]),t._v(" 也是完全可以接受的,这里是一个例子:")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("net"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("IpAddr"),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),e("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" home"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" IpAddr "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{attrs:{class:"token string"}},[t._v('"127.0.0.1"')]),e("span",{attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{attrs:{class:"token function"}},[t._v("parse")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{attrs:{class:"token function"}},[t._v("unwrap")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("我们通过解析一个硬编码的字符来创建一个 "),e("code",[t._v("IpAddr")]),t._v(" 实例。可以看出 "),e("code",[t._v("127.0.0.1")]),t._v(" 是一个有效的 IP 地址,所以这里使用 "),e("code",[t._v("unwrap")]),t._v(" 是可以接受的。然而,拥有一个硬编码的有效的字符串也不能改变 "),e("code",[t._v("parse")]),t._v(" 方法的返回值类型:它仍然是一个 "),e("code",[t._v("Result")]),t._v(" 值,而编译器仍然就好像还是有可能出现 "),e("code",[t._v("Err")]),t._v(" 成员那样要求我们处理 "),e("code",[t._v("Result")]),t._v(",因为编译器还没有智能到可以识别出这个字符串总是一个有效的 IP 地址。如果 IP 地址字符串来源于用户而不是硬编码进程序中的话,那么就 "),e("strong",[t._v("确实")]),t._v(" 有失败的可能性,这时就绝对需要我们以一种更健壮的方式处理 "),e("code",[t._v("Result")]),t._v(" 了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"错误处理指导原则"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#错误处理指导原则","aria-hidden":"true"}},[this._v("#")]),this._v(" 错误处理指导原则")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在当有可能会导致有害状态的情况下建议使用 "),s("code",[this._v("panic!")]),this._v(" —— 在这里,有害状态是指当一些假设、保证、协议或不可变性被打破的状态,例如无效的值、自相矛盾的值或者被传递了不存在的值 —— 外加如下几种情况:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[this._v("有害状态并不包含 "),s("strong",[this._v("预期")]),this._v(" 会偶尔发生的错误")]),this._v(" "),s("li",[this._v("之后的代码的运行依赖于处于这种有害状态")]),this._v(" "),s("li",[this._v("当没有可行的手段来将有害状态信息编码进所使用的类型中的情况")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果别人调用你的代码并传递了一个没有意义的值,最好的情况也许就是 "),s("code",[this._v("panic!")]),this._v(" 并警告使用你的库的人他的代码中有 bug 以便他能在开发时就修复它。类似的,"),s("code",[this._v("panic!")]),this._v(" 通常适合调用不能够控制的外部代码时,这时无法修复其返回的无效状态。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("然而当错误预期会出现时,返回 "),e("code",[t._v("Result")]),t._v(" 仍要比调用 "),e("code",[t._v("panic!")]),t._v(" 更为合适。这样的例子包括解析器接收到错误数据,或者 HTTP 请求返回一个表明触发了限流的状态。在这些例子中,应该通过返回 "),e("code",[t._v("Result")]),t._v(" 来表明失败预期是可能的,这样将有害状态向上传播,调用者就可以决定该如何处理这个问题。使用 "),e("code",[t._v("panic!")]),t._v(" 来处理这些情况就不是最好的选择。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("当代码对值进行操作时,应该首先验证值是有效的,并在其无效时 "),e("code",[t._v("panic!")]),t._v("。这主要是出于安全的原因:尝试操作无效数据会暴露代码漏洞,这就是标准库在尝试越界访问数组时会 "),e("code",[t._v("panic!")]),t._v(" 的主要原因:尝试访问不属于当前数据结构的内存是一个常见的安全隐患。函数通常都遵循 "),e("strong",[t._v("契约")]),t._v("("),e("em",[t._v("contracts")]),t._v("):他们的行为只有在输入满足特定条件时才能得到保证。当违反契约时 panic 是有道理的,因为这通常代表调用方的 bug,而且这也不是那种你希望调用方必须处理的错误。事实上也没有合理的方式来恢复调用方的代码:调用方的 "),e("strong",[t._v("程序员")]),t._v(" 需要修复其代码。函数的契约,尤其是当违反它会造成 panic 的契约,应该在函数的 API 文档中得到解释。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("虽然在所有函数中都拥有许多错误检查是冗长而烦人的。幸运的是,可以利用 Rust 的类型系统(以及编译器的类型检查)为你进行很多检查。如果函数有一个特定类型的参数,可以在知晓编译器已经确保其拥有一个有效值的前提下进行你的代码逻辑。例如,如果你使用了一个不同于 "),e("code",[t._v("Option")]),t._v(" 的类型,而且程序期望它是 "),e("strong",[t._v("有值")]),t._v(" 的并且不是 "),e("strong",[t._v("空值")]),t._v("。你的代码无需处理 "),e("code",[t._v("Some")]),t._v(" 和 "),e("code",[t._v("None")]),t._v(" 这两种情况,它只会有一种情况就是绝对会有一个值。尝试向函数传递空值的代码甚至根本不能编译,所以你的函数在运行时没有必要判空。另外一个例子是使用像 "),e("code",[t._v("u32")]),t._v(" 这样的无符号整型,也会确保它永远不为负。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("一种实现方式是将猜测解析成 "),s("code",[this._v("i32")]),this._v(" 而不仅仅是 "),s("code",[this._v("u32")]),this._v(",来默许输入负数,接着检查数字是否在范围内:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('loop {\n // --snip--\n\n let guess: i32 = match guess.trim().parse() {\n Ok(num) => num,\n Err(_) => continue,\n };\n\n if guess < 1 || guess > 100 {\n println!("The secret number will be between 1 and 100.");\n continue;\n }\n\n match guess.cmp(&secret_number) {\n // --snip--\n}\n')])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[e("code",[t._v("if")]),t._v(" 表达式检查了值是否超出范围,告诉用户出了什么问题,并调用 "),e("code",[t._v("continue")]),t._v(" 开始下一次循环,请求另一个猜测。"),e("code",[t._v("if")]),t._v(" 表达式之后,就可以在知道 "),e("code",[t._v("guess")]),t._v(" 在 1 到 100 之间的情况下与秘密数字作比较了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("相反我们可以创建一个新类型来将验证放入创建其实例的函数中,而不是到处重复这些检查。这样就可以安全的在函数签名中使用新类型并相信他们接收到的值。示例 9-10 中展示了一个定义 "),s("code",[this._v("Guess")]),this._v(" 类型的方法,只有在 "),s("code",[this._v("new")]),this._v(" 函数接收到 1 到 100 之间的值时才会创建 "),s("code",[this._v("Guess")]),this._v(" 的实例:")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),e("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Guess "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Guess "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("new")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Guess "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" value "),e("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("1")]),t._v(" "),e("span",{attrs:{class:"token operator"}},[t._v("||")]),t._v(" value "),e("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("100")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("panic!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token string"}},[t._v('"Guess value must be between 1 and 100, got {}."')]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" value"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n Guess "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),e("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("value")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token operator"}},[t._v("&")]),e("span",{attrs:{class:"token keyword"}},[t._v("self")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("self")]),e("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 9-10:一个 "),s("code",[this._v("Guess")]),this._v(" 类型,它只在值位于 1 和 100 之间时才继续")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("首先,我们定义了一个包含 "),s("code",[this._v("u32")]),this._v(" 类型字段 "),s("code",[this._v("value")]),this._v(" 的结构体 "),s("code",[this._v("Guess")]),this._v("。这里是储存猜测值的地方。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("接着在 "),e("code",[t._v("Guess")]),t._v(" 上实现了一个叫做 "),e("code",[t._v("new")]),t._v(" 的关联函数来创建 "),e("code",[t._v("Guess")]),t._v(" 的实例。"),e("code",[t._v("new")]),t._v(" 定义为接收一个 "),e("code",[t._v("u32")]),t._v(" 类型的参数 "),e("code",[t._v("value")]),t._v(" 并返回一个 "),e("code",[t._v("Guess")]),t._v("。"),e("code",[t._v("new")]),t._v(" 函数中代码的测试确保了其值是在 1 到 100 之间的。如果 "),e("code",[t._v("value")]),t._v(" 没有通过测试则调用 "),e("code",[t._v("panic!")]),t._v(",这会警告调用这个函数的程序员有一个需要修改的 bug,因为创建一个 "),e("code",[t._v("value")]),t._v(" 超出范围的 "),e("code",[t._v("Guess")]),t._v(" 将会违反 "),e("code",[t._v("Guess::new")]),t._v(" 所遵循的契约。"),e("code",[t._v("Guess::new")]),t._v(" 会出现 panic 的条件应该在其公有 API 文档中被提及;第十四章会涉及到在 API 文档中表明 "),e("code",[t._v("panic!")]),t._v(" 可能性的相关规则。如果 "),e("code",[t._v("value")]),t._v(" 通过了测试,我们新建一个 "),e("code",[t._v("Guess")]),t._v(",其字段 "),e("code",[t._v("value")]),t._v(" 将被设置为参数 "),e("code",[t._v("value")]),t._v(" 的值,接着返回这个 "),e("code",[t._v("Guess")]),t._v("。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("接着,我们实现了一个借用了 "),e("code",[t._v("self")]),t._v(" 的方法 "),e("code",[t._v("value")]),t._v(",它没有任何其他参数并返回一个 "),e("code",[t._v("u32")]),t._v("。这类方法有时被称为 "),e("em",[t._v("getter")]),t._v(",因为它的目的就是返回对应字段的数据。这样的公有方法是必要的,因为 "),e("code",[t._v("Guess")]),t._v(" 结构体的 "),e("code",[t._v("value")]),t._v(" 字段是私有的。私有的字段 "),e("code",[t._v("value")]),t._v(" 是很重要的,这样使用 "),e("code",[t._v("Guess")]),t._v(" 结构体的代码将不允许直接设置 "),e("code",[t._v("value")]),t._v(" 的值:调用者 "),e("strong",[t._v("必须")]),t._v(" 使用 "),e("code",[t._v("Guess::new")]),t._v(" 方法来创建一个 "),e("code",[t._v("Guess")]),t._v(" 的实例,这就确保了不会存在一个 "),e("code",[t._v("value")]),t._v(" 没有通过 "),e("code",[t._v("Guess::new")]),t._v(" 函数的条件检查的 "),e("code",[t._v("Guess")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如此获取一个参数并只返回 1 到 100 之间数字的函数就可以声明为获取或返回一个 "),s("code",[this._v("Guess")]),this._v(",而不是 "),s("code",[this._v("u32")]),this._v(",同时其函数体中也无需进行任何额外的检查。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("Rust 的错误处理功能被设计为帮助你编写更加健壮的代码。"),e("code",[t._v("panic!")]),t._v(" 宏代表一个程序无法处理的状态,并停止执行而不是使用无效或不正确的值继续处理。Rust 类型系统的 "),e("code",[t._v("Result")]),t._v(" 枚举代表操作可能会在一种可以恢复的情况下失败。可以使用 "),e("code",[t._v("Result")]),t._v(" 来告诉代码调用者他需要处理潜在的成功或失败。在适当的场景使用 "),e("code",[t._v("panic!")]),t._v(" 和 "),e("code",[t._v("Result")]),t._v(" 将会使你的代码在面对不可避免的错误时显得更加可靠。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在我们已经见识过了标准库中 "),s("code",[this._v("Option")]),this._v(" 和 "),s("code",[this._v("Result")]),this._v(" 泛型枚举的能力了,在下一章让我们聊聊泛型是如何工作的,以及如何在你的代码中使用他们。")])}],!1,null,null,null);_.options.__file="ch09-03-to-panic-or-not-to-panic.md";s.default=_.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/51.d4e57e23.js b/src/.vuepress/dist/assets/js/51.d4e57e23.js
new file mode 100644
index 0000000..10665c5
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/51.d4e57e23.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[51],{219:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch10-00-generics.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch10-00-generics.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),n("p",[t._v("首先,我们将回顾一下提取函数以减少代码重复的机制。接着使用一个只在参数类型上不同的泛型函数来实现相同的功能。我们也会讲到结构体和枚举定义中的泛型。")]),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),n("p",[t._v("在介绍泛型语法之前,首先来回顾一个不使用泛型的处理重复的技术:提取一个函数。当熟悉了这个技术以后,我们将使用相同的机制来提取一个泛型函数!如同你识别出可以提取到函数中重复代码那样,你也会开始识别出能够使用泛型的重复代码。")]),t._v(" "),n("p",[t._v("考虑一下这个寻找列表中最大值的小程序,如示例 10-1 所示:")]),t._v(" "),t._m(6),t._v(" "),t._m(7),t._m(8),t._v(" "),t._m(9),t._v(" "),n("p",[t._v("如果需要在两个不同的列表中寻找最大值,我们可以重复示例 10-1 中的代码,这样程序中就会存在两段相同逻辑的代码,如示例 10-2 所示:")]),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),n("p",[t._v("虽然代码能够执行,但是重复的代码是冗余且容易出错的,并且意味着当更新逻辑时需要修改多处地方的代码。")]),t._v(" "),n("p",[t._v("为了消除重复,我们可以创建一层抽象,在这个例子中将表现为一个获取任意整型列表作为参数并对其进行处理的函数。这将增加代码的简洁性并让我们将表达和推导寻找列表中最大值的这个概念与使用这个概念的特定位置相互独立。")]),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),n("p",[t._v("从示例 10-2 到示例 10-3 中涉及的机制经历了如下几步:")]),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"泛型、trait-和生命周期"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#泛型、trait-和生命周期","aria-hidden":"true"}},[this._v("#")]),this._v(" 泛型、trait 和生命周期")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("每一个编程语言都有高效的处理重复概念的工具.在 Rust 中其工具之一就是 "),s("strong",[this._v("泛型")]),this._v("("),s("em",[this._v("generics")]),this._v(")。泛型是具体类型或其他属性的抽象替代。我们可以表达泛型的属性,比如他们的行为或如何与其他泛型相关联,而不需要在编写和编译代码时知道他们在这里实际上代表什么。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("同理为了编写一份可以用于多种具体值的代码,函数并不知道其参数为何值,这时就可以让函数获取泛型而不是像 "),n("code",[t._v("i32")]),t._v(" 或 "),n("code",[t._v("String")]),t._v(" 这样的具体值。我们已经使用过第六章的 "),n("code",[t._v("Option")]),t._v(",第八章的 "),n("code",[t._v("Vec")]),t._v(" 和 "),n("code",[t._v("HashMap")]),t._v(",以及第九章的 "),n("code",[t._v("Result")]),t._v(" 这些泛型了。本章会探索如何使用泛型定义我们自己的类型、函数和方法!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("之后,我们讨论 "),s("em",[this._v("trait")]),this._v(",这是一个定义泛型行为的方法。trait 可以与泛型结合来将泛型限制为拥有特定行为的类型,而不是任意类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("最后介绍 "),s("strong",[this._v("生命周期")]),this._v("("),s("em",[this._v("lifetimes")]),this._v("),它是一类允许我们向编译器提供引用如何相互关联的泛型。Rust 的生命周期功能允许在很多场景下借用值的同时仍然使编译器能够检查这些引用的有效性。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"提取函数来减少重复"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#提取函数来减少重复","aria-hidden":"true"}},[this._v("#")]),this._v(" 提取函数来减少重复")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number_list "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("34")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("50")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("25")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("100")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("65")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" number_list"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" number "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" number_list "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" largest "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" number"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The largest number is {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" largest"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("largest"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("100")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-1:在一个数字列表中寻找最大值的函数")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这段代码获取一个整型列表,存放在变量 "),n("code",[t._v("number_list")]),t._v(" 中。它将列表的第一项放入了变量 "),n("code",[t._v("largest")]),t._v(" 中。接着遍历了列表中的所有数字,如果当前值大于 "),n("code",[t._v("largest")]),t._v(" 中储存的值,将 "),n("code",[t._v("largest")]),t._v(" 替换为这个值。如果当前值小于目前为止的最大值,"),n("code",[t._v("largest")]),t._v(" 保持不变。当列表中所有值都被考虑到之后,"),n("code",[t._v("largest")]),t._v(" 将会是最大值,在这里也就是 100。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number_list "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("34")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("50")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("25")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("100")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("65")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" number_list"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" number "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" number_list "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" largest "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" number"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The largest number is {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" largest"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number_list "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("102")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("34")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("6000")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("89")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("54")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("43")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("8")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" number_list"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" number "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" number_list "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" number "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" largest "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" number"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The largest number is {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" largest"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-2:寻找 "),s("strong",[this._v("两个")]),this._v(" 数字列表最大值的代码")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在示例 10-3 的程序中将寻找最大值的代码提取到了一个叫做 "),s("code",[this._v("largest")]),this._v(" 的函数中。这个程序可以找出两个不同数字列表的最大值,不过示例 10-1 中的代码只存在于一个位置:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("largest")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("list"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i32"),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" list"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("item "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" list"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" item "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" largest "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" item"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n largest\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number_list "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("34")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("50")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("25")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("100")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("65")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" result "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("largest")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("number_list"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The largest number is {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" result"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("100")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number_list "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("102")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("34")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("6000")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("89")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("54")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("43")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("8")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" result "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("largest")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("number_list"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The largest number is {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" result"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("6000")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-3:抽象后的寻找两个数字列表最大值的代码")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这个函数有一个参数 "),n("code",[t._v("list")]),t._v(",它代表会传递给函数的任何具体的 "),n("code",[t._v("i32")]),t._v("值的 slice。函数定义中的 "),n("code",[t._v("list")]),t._v(" 代表任何 "),n("code",[t._v("&[i32]")]),t._v("。当调用 "),n("code",[t._v("largest")]),t._v(" 函数时,其代码实际上运行于我们传递的特定值上。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ol",[s("li",[this._v("找出重复代码。")]),this._v(" "),s("li",[this._v("将重复代码提取到了一个函数中,并在函数签名中指定了代码中的输入和返回值。")]),this._v(" "),s("li",[this._v("将重复代码的两个实例,改为调用函数。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在不同的场景使用不同的方式,我们也可以利用相同的步骤和泛型来减少重复代码。与函数体可以在抽象"),s("code",[this._v("list")]),this._v("而不是特定值上操作的方式相同,泛型允许代码对抽象类型进行操作。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果我们有两个函数,一个寻找一个 "),s("code",[this._v("i32")]),this._v(" 值的 slice 中的最大项而另一个寻找 "),s("code",[this._v("char")]),this._v(" 值的 slice 中的最大项该怎么办?该如何消除重复呢?让我们拭目以待!")])}],!1,null,null,null);e.options.__file="ch10-00-generics.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/52.323a010e.js b/src/.vuepress/dist/assets/js/52.323a010e.js
new file mode 100644
index 0000000..5df671b
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/52.323a010e.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[52],{218:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch10-01-syntax.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch10-01-syntax.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),n("p",[t._v("我们可以使用泛型为像函数签名或结构体这样的项创建定义,这样它们就可以用于多种不同的具体数据类型。让我们看看如何使用泛型定义函数、结构体、枚举和方法,然后我们将讨论泛型如何影响代码性能。")]),t._v(" "),t._m(1),t._v(" "),n("p",[t._v("当使用泛型定义函数时,我们在函数签名中通常为参数和返回值指定数据类型的位置放置泛型。以这种方式编写的代码将更灵活并能向函数调用者提供更多功能,同时不引入重复代码。")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._m(14),t._v(" "),n("p",[t._v("如果现在就尝试编译这些代码,会出现如下错误:")]),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._m(22),t._v(" "),n("p",[t._v("其语法类似于函数定义中使用泛型。首先,必须在结构体名称后面的尖括号中声明泛型参数的名称。接着在结构体定义中可以指定具体数据类型的位置使用泛型类型。")]),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),t._m(40),t._v(" "),n("p",[t._v("当发现代码中有多个只有存放的值的类型有所不同的结构体或枚举定义时,你就应该像之前的函数定义中那样引入泛型类型来减少重复代码。")]),t._v(" "),t._m(41),t._v(" "),n("p",[t._v("也可以在定义中使用泛型在结构体和枚举上实现方法(像第五章那样)。")]),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._m(55),t._v(" "),t._m(56),t._v(" "),t._m(57),t._v(" "),t._m(58),t._v(" "),n("p",[t._v("在阅读本部分内容的同时,你可能会好奇使用泛型类型参数是否会有运行时消耗。好消息是:Rust 实现了泛型,使得使用泛型类型参数的代码相比使用具体类型并没有任何速度上的损失。")]),t._v(" "),t._m(59),t._v(" "),n("p",[t._v("编译器所做的工作正好与示例 10-5 中我们创建泛型函数的步骤相反。编译器寻找所有泛型代码被调用的位置并使用泛型代码针对具体类型生成代码。")]),t._v(" "),t._m(60),t._v(" "),t._m(61),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._v(" "),t._m(65),n("p",[t._v("我们可以使用泛型来编写不重复的代码,而 Rust 将会为每一个实例编译其特定类型的代码。这意味着在使用泛型时没有运行时开销;当代码运行,它的执行效率就跟好像手写每个具体定义的重复代码一样。这个单态化过程正是 Rust 泛型在运行时极其高效的原因。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"泛型数据类型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#泛型数据类型","aria-hidden":"true"}},[this._v("#")]),this._v(" 泛型数据类型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"在函数定义中使用泛型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#在函数定义中使用泛型","aria-hidden":"true"}},[this._v("#")]),this._v(" 在函数定义中使用泛型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("回到 "),s("code",[this._v("largest")]),this._v(" 函数上,示例 10-4 中展示了两个提供了相同的寻找 slice 中最大值功能的函数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("largest_i32")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("list"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i32"),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" list"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("item "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" list"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" item "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" largest "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" item"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n largest\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("largest_char")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("list"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("char"),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" char "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" list"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("item "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" list"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" item "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" largest "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n largest "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" item"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n largest\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number_list "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("34")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("50")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("25")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("100")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("65")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" result "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("largest_i32")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("number_list"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The largest number is {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" result"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("100")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" char_list "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token char string"}},[t._v("'y'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token char string"}},[t._v("'m'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token char string"}},[t._v("'a'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token char string"}},[t._v("'q'")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" result "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("largest_char")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("char_list"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The largest char is {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" result"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token char string"}},[t._v("'y'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-4:两个只在名称和签名中类型有所不同的函数")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("largest_i32")]),t._v(" 函数是从示例 10-3 中提取的寻找 slice 中 "),n("code",[t._v("i32")]),t._v(" 最大值的函数。"),n("code",[t._v("largest_char")]),t._v(" 函数寻找 slice 中 "),n("code",[t._v("char")]),t._v(" 的最大值:这两个函数有着相同的代码,所以让我们在一个单独的函数中引入泛型参数来消除重复。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为了参数化要定义的函数的签名中的类型,我们需要像给函数的值参数起名那样为这类型参数起一个名字。任何标识符都可以作为类型参数名。不过选择 "),s("code",[this._v("T")]),this._v(" 是因为 Rust 的习惯是让变量名尽量短,通常就只有一个字母,同时 Rust 类型命名规范是骆驼命名法(CamelCase)。"),s("code",[this._v("T")]),this._v(" 作为 “type” 的缩写是大部分 Rust 程序员的首选。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当需要在函数体中使用一个参数时,必须在函数签名中声明这个参数以便编译器能知道函数体中这个名称的意义。同理,当在函数签名中使用一个类型参数时,必须在使用它之前就声明它。为了定义泛型版本的 "),s("code",[this._v("largest")]),this._v(" 函数,类型参数声明位于函数名称与参数列表中间的尖括号 "),s("code",[this._v("<>")]),this._v(" 中,像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn largest(list: &[T]) -> T {\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这可以理解为:函数 "),n("code",[t._v("largest")]),t._v(" 有泛型类型 "),n("code",[t._v("T")]),t._v("。它有一个参数 "),n("code",[t._v("list")]),t._v(",它的类型是一个 "),n("code",[t._v("T")]),t._v(" 值的 slice。"),n("code",[t._v("largest")]),t._v(" 函数将会返回一个与 "),n("code",[t._v("T")]),t._v(" 相同类型的值。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 10-5 展示一个在签名中使用了泛型的统一的 "),n("code",[t._v("largest")]),t._v(" 函数定义。该示例也向我们展示了如何对 "),n("code",[t._v("i32")]),t._v(" 值的 slice 或 "),n("code",[t._v("char")]),t._v(" 值的 slice 调用 "),n("code",[t._v("largest")]),t._v(" 函数。注意这些代码还不能编译,不过本章稍后部分会修复错误。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn largest(list: &[T]) -> T {\n let mut largest = list[0];\n\n for &item in list.iter() {\n if item > largest {\n largest = item;\n }\n }\n\n largest\n}\n\nfn main() {\n let number_list = vec![34, 50, 25, 100, 65];\n\n let result = largest(&number_list);\n println!(\"The largest number is {}\", result);\n\n let char_list = vec!['y', 'm', 'a', 'q'];\n\n let result = largest(&char_list);\n println!(\"The largest char is {}\", result);\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-5:一个还不能编译的使用泛型参数的 "),s("code",[this._v("largest")]),this._v(" 函数定义")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0369]: binary operation `>` cannot be applied to type `T`\n --\x3e src/main.rs:5:12\n |\n5 | if item > largest {\n | ^^^^^^^^^^^^^^\n |\n = note: an implementation of `std::cmp::PartialOrd` might be missing for `T`\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注释中提到了 "),n("code",[t._v("std::cmp::PartialOrd")]),t._v(",这是一个 "),n("em",[t._v("trait")]),t._v("。下一部分会讲到 trait。不过简单来说,这个错误表明 "),n("code",[t._v("largest")]),t._v(" 的函数体不能适用于 "),n("code",[t._v("T")]),t._v(" 的所有可能的类型。因为在函数体需要比较 "),n("code",[t._v("T")]),t._v(" 类型的值,不过它只能用于我们知道如何排序的类型。为了开启比较功能,标准库中定义的 "),n("code",[t._v("std::cmp::PartialOrd")]),t._v(" trait 可以实现类型的比较功能(查看附录 C 获取该 trait 的更多信息)。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("标准库中定义的 "),s("code",[this._v("std::cmp::PartialOrd")]),this._v(" trait 可以实现类型的比较功能。在 “trait bound” 部分会讲解如何指定泛型实现特定的 trait,不过让我们先探索其他使用泛型参数的方法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"结构体定义中的泛型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#结构体定义中的泛型","aria-hidden":"true"}},[this._v("#")]),this._v(" 结构体定义中的泛型")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("同样也可以使用 "),n("code",[t._v("<>")]),t._v(" 语法来定义拥有一个或多个泛型参数类型字段的结构体。示例 10-6 展示了如何定义和使用一个可以存放任何类型的 "),n("code",[t._v("x")]),t._v(" 和 "),n("code",[t._v("y")]),t._v(" 坐标值的结构体 "),n("code",[t._v("Point")]),t._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Point"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" integer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" float "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1.0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("4.0")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("span",{staticClass:"caption"},[t._v("示例 10-6:"),n("code",[t._v("Point")]),t._v(" 结构体存放了两个 "),n("code",[t._v("T")]),t._v(" 类型的值 "),n("code",[t._v("x")]),t._v(" 和 "),n("code",[t._v("y")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意 "),n("code",[t._v("Point")]),t._v(" 的定义中只使用了一个泛型类型,这个定义表明结构体 "),n("code",[t._v("Point")]),t._v(" 对于一些类型 "),n("code",[t._v("T")]),t._v(" 是泛型的,而且字段 "),n("code",[t._v("x")]),t._v(" 和 "),n("code",[t._v("y")]),t._v(" "),n("strong",[t._v("都是")]),t._v(" 相同类型的,无论它具体是何类型。如果尝试创建一个有不同类型值的 "),n("code",[t._v("Point")]),t._v(" 的实例,像示例 10-7 中的代码就不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("struct Point {\n x: T,\n y: T,\n}\n\nfn main() {\n let wont_work = Point { x: 5, y: 4.0 };\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-7:字段 "),s("code",[this._v("x")]),this._v(" 和 "),s("code",[this._v("y")]),this._v(" 必须是相同类型,因为他们都有相同的泛型类型 "),s("code",[this._v("T")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在这个例子中,当把整型值 5 赋值给 "),n("code",[t._v("x")]),t._v(" 时,就告诉了编译器这个 "),n("code",[t._v("Point")]),t._v(" 实例中的泛型 "),n("code",[t._v("T")]),t._v(" 是整型的。接着指定 "),n("code",[t._v("y")]),t._v(" 为 4.0,它被定义为与 "),n("code",[t._v("x")]),t._v(" 相同类型,就会得到一个像这样的类型不匹配错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0308]: mismatched types\n --\x3e src/main.rs:7:38\n |\n7 | let wont_work = Point { x: 5, y: 4.0 };\n | ^^^ expected integral variable, found\nfloating-point variable\n |\n = note: expected type `{integer}`\n found type `{float}`\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如果想要定义一个 "),n("code",[t._v("x")]),t._v(" 和 "),n("code",[t._v("y")]),t._v(" 可以有不同类型且仍然是泛型的 "),n("code",[t._v("Point")]),t._v(" 结构体,我们可以使用多个泛型类型参数。在示例 10-8 中,我们修改 "),n("code",[t._v("Point")]),t._v(" 的定义为拥有两个泛型类型 "),n("code",[t._v("T")]),t._v(" 和 "),n("code",[t._v("U")]),t._v("。其中字段 "),n("code",[t._v("x")]),t._v(" 是 "),n("code",[t._v("T")]),t._v(" 类型的,而字段 "),n("code",[t._v("y")]),t._v(" 是 "),n("code",[t._v("U")]),t._v(" 类型的:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Point"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" U"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" U"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" both_integer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" both_float "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1.0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("4.0")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" integer_and_float "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("4.0")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-8:使用两个泛型的 "),s("code",[this._v("Point")]),this._v(",这样 "),s("code",[this._v("x")]),this._v(" 和 "),s("code",[this._v("y")]),this._v(" 可能是不同类型")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在所有这些 "),s("code",[this._v("Point")]),this._v(" 实例都是被允许的了!你可以在定义中使用任意多的泛型类型参数,不过太多的话代码将难以阅读和理解。当你的代码中需要许多泛型类型时,它可能表明你的代码需要重组为更小的部分。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"枚举定义中的泛型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#枚举定义中的泛型","aria-hidden":"true"}},[this._v("#")]),this._v(" 枚举定义中的泛型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("类似于结构体,枚举也可以在其成员中存放泛型数据类型。第六章我们使用过了标准库提供的 "),s("code",[this._v("Option")]),this._v(" 枚举,让我们再看看:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n None"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("现在这个定义看起来就更容易理解了。如你所见 "),n("code",[t._v("Option")]),t._v(" 是一个拥有泛型 "),n("code",[t._v("T")]),t._v(" 的枚举,它有两个成员:"),n("code",[t._v("Some")]),t._v(",它存放了一个类型 "),n("code",[t._v("T")]),t._v(" 的值,和不存在任何值的"),n("code",[t._v("None")]),t._v("。通过 "),n("code",[t._v("Option")]),t._v(" 枚举可以表达有一个可能的值的抽象概念,同时因为 "),n("code",[t._v("Option")]),t._v(" 是泛型的,无论这个可能的值是什么类型都可以使用这个抽象。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("枚举也可以拥有多个泛型类型。第九章使用过的 "),s("code",[this._v("Result")]),this._v(" 枚举定义就是一个这样的例子:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" E"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("E"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Result")]),t._v(" 枚举有两个泛型类型,"),n("code",[t._v("T")]),t._v(" 和 "),n("code",[t._v("E")]),t._v("。"),n("code",[t._v("Result")]),t._v(" 有两个成员:"),n("code",[t._v("Ok")]),t._v(",它存放一个类型 "),n("code",[t._v("T")]),t._v(" 的值,而 "),n("code",[t._v("Err")]),t._v(" 则存放一个类型 "),n("code",[t._v("E")]),t._v(" 的值。这个定义使得 "),n("code",[t._v("Result")]),t._v(" 枚举能很方便的表达任何可能成功(返回 "),n("code",[t._v("T")]),t._v(" 类型的值)也可能失败(返回 "),n("code",[t._v("E")]),t._v(" 类型的值)的操作。回忆一下示例 9-2 中打开一个文件的场景:当文件被成功打开 "),n("code",[t._v("T")]),t._v(" 被放入了 "),n("code",[t._v("std::fs::File")]),t._v(" 类型而当打开文件出现问题时 "),n("code",[t._v("E")]),t._v(" 被放入了 "),n("code",[t._v("std::io::Error")]),t._v(" 类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"方法定义中的泛型"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#方法定义中的泛型","aria-hidden":"true"}},[this._v("#")]),this._v(" 方法定义中的泛型")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以像第五章介绍的那样来为其定义中带有泛型的结构体或枚举实现方法。示例 10-9 中展示了示例 10-6 中定义的结构体 "),s("code",[this._v("Point")]),this._v(",和在其上实现的名为 "),s("code",[this._v("x")]),this._v(" 的方法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Point"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" Point"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("x")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("T "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" p "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"p.x = {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" p"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("x")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("span",{staticClass:"caption"},[t._v("示例 10-9:在 "),n("code",[t._v("Point")]),t._v(" 结构体上实现方法 "),n("code",[t._v("x")]),t._v(",它返回 "),n("code",[t._v("T")]),t._v(" 类型的字段 "),n("code",[t._v("x")]),t._v(" 的引用")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里在 "),s("code",[this._v("Point")]),this._v(" 上定义了一个叫做 "),s("code",[this._v("x")]),this._v(" 的方法来返回字段 "),s("code",[this._v("x")]),this._v(" 中数据的引用:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意必须在 "),n("code",[t._v("impl")]),t._v(" 后面声明 "),n("code",[t._v("T")]),t._v(",这样就可以在 "),n("code",[t._v("Point")]),t._v(" 上实现的方法中使用它了。在 "),n("code",[t._v("impl")]),t._v(" 之后声明泛型 "),n("code",[t._v("T")]),t._v(" ,这样 Rust 就知道 "),n("code",[t._v("Point")]),t._v(" 的尖括号中的类型是泛型而不是具体类型。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("例如,可以选择为 "),n("code",[t._v("Point")]),t._v(" 实例实现方法,而不是为泛型 "),n("code",[t._v("Point")]),t._v(" 实例。示例 10-10 展示了一个没有在 "),n("code",[t._v("impl")]),t._v(" 之后(的尖括号)声明泛型的例子,这里使用了一个具体类型,"),n("code",[t._v("f32")]),t._v(":")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Point"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Point"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("f32"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("distance_from_origin")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" f32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("powi")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("y"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("powi")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("sqrt")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-10:构建一个只用于拥有泛型参数 "),s("code",[this._v("T")]),this._v(" 的结构体的具体类型的 "),s("code",[this._v("impl")]),this._v(" 块")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这段代码意味着 "),n("code",[t._v("Point")]),t._v(" 类型会有一个方法 "),n("code",[t._v("distance_from_origin")]),t._v(",而其他 "),n("code",[t._v("T")]),t._v(" 不是 "),n("code",[t._v("f32")]),t._v(" 类型的 "),n("code",[t._v("Point")]),t._v(" 实例则没有定义此方法。这个方法计算点实例与坐标 (0.0, 0.0) 之间的距离,并使用了只能用于浮点型的数学运算符。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("结构体定义中的泛型类型参数并不总是与结构体方法签名中使用的泛型是同一类型。示例 10-11 中在示例 10-8 中的结构体 "),n("code",[t._v("Point")]),t._v(" 上定义了一个方法 "),n("code",[t._v("mixup")]),t._v("。这个方法获取另一个 "),n("code",[t._v("Point")]),t._v(" 作为参数,而它可能与调用 "),n("code",[t._v("mixup")]),t._v(" 的 "),n("code",[t._v("self")]),t._v(" 是不同的 "),n("code",[t._v("Point")]),t._v(" 类型。这个方法用 "),n("code",[t._v("self")]),t._v(" 的 "),n("code",[t._v("Point")]),t._v(" 类型的 "),n("code",[t._v("x")]),t._v(" 值(类型 "),n("code",[t._v("T")]),t._v(")和参数的 "),n("code",[t._v("Point")]),t._v(" 类型的 "),n("code",[t._v("y")]),t._v(" 值(类型 "),n("code",[t._v("W")]),t._v(")来创建一个新 "),n("code",[t._v("Point")]),t._v(" 类型的实例:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Point"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" U"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" U"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" U"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" Point"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" U"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" mixup"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("V"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" W"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" other"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Point"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("V"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" W"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Point"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" W"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Point "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" other"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("y"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" p1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10.4")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" p2 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Point "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"Hello"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token char string"}},[t._v("'c'")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" p3 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" p1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("mixup")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p2"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"p3.x = {}, p3.y = {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" p3"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" p3"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("y"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-11:方法使用了与结构体定义中不同类型的泛型")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 "),n("code",[t._v("main")]),t._v(" 函数中,定义了一个有 "),n("code",[t._v("i32")]),t._v(" 类型的 "),n("code",[t._v("x")]),t._v("(其值为 "),n("code",[t._v("5")]),t._v(")和 "),n("code",[t._v("f64")]),t._v(" 的 "),n("code",[t._v("y")]),t._v("(其值为 "),n("code",[t._v("10.4")]),t._v(")的 "),n("code",[t._v("Point")]),t._v("。"),n("code",[t._v("p2")]),t._v(" 则是一个有着字符串 slice 类型的 "),n("code",[t._v("x")]),t._v("(其值为 "),n("code",[t._v('"Hello"')]),t._v(")和 "),n("code",[t._v("char")]),t._v(" 类型的 "),n("code",[t._v("y")]),t._v("(其值为"),n("code",[t._v("c")]),t._v(")的 "),n("code",[t._v("Point")]),t._v("。在 "),n("code",[t._v("p1")]),t._v(" 上以 "),n("code",[t._v("p2")]),t._v(" 作为参数调用 "),n("code",[t._v("mixup")]),t._v(" 会返回一个 "),n("code",[t._v("p3")]),t._v(",它会有一个 "),n("code",[t._v("i32")]),t._v(" 类型的 "),n("code",[t._v("x")]),t._v(",因为 "),n("code",[t._v("x")]),t._v(" 来自 "),n("code",[t._v("p1")]),t._v(",并拥有一个 "),n("code",[t._v("char")]),t._v(" 类型的 "),n("code",[t._v("y")]),t._v(",因为 "),n("code",[t._v("y")]),t._v(" 来自 "),n("code",[t._v("p2")]),t._v("。"),n("code",[t._v("println!")]),t._v(" 会打印出 "),n("code",[t._v("p3.x = 5, p3.y = c")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这个例子的目的是展示一些泛型通过 "),n("code",[t._v("impl")]),t._v(" 声明而另一些通过方法定义声明的情况。这里泛型参数 "),n("code",[t._v("T")]),t._v(" 和 "),n("code",[t._v("U")]),t._v(" 声明于 "),n("code",[t._v("impl")]),t._v(" 之后,因为他们与结构体定义相对应。而泛型参数 "),n("code",[t._v("V")]),t._v(" 和 "),n("code",[t._v("W")]),t._v(" 声明于 "),n("code",[t._v("fn mixup")]),t._v(" 之后,因为他们只是相对于方法本身的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"泛型代码的性能"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#泛型代码的性能","aria-hidden":"true"}},[this._v("#")]),this._v(" 泛型代码的性能")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 通过在编译时进行泛型代码的 "),s("strong",[this._v("单态化")]),this._v("("),s("em",[this._v("monomorphization")]),this._v(")来保证效率。单态化是一个通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们看看一个使用标准库中 "),s("code",[this._v("Option")]),this._v(" 枚举的例子:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" integer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" float "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5.0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当 Rust 编译这些代码的时候,它会进行单态化。编译器会读取传递给 "),n("code",[t._v("Option")]),t._v(" 的值并发现有两种 "),n("code",[t._v("Option")]),t._v(":一个对应 "),n("code",[t._v("i32")]),t._v(" 另一个对应 "),n("code",[t._v("f64")]),t._v("。为此,它会将泛型定义 "),n("code",[t._v("Option")]),t._v(" 展开为 "),n("code",[t._v("Option_i32")]),t._v(" 和 "),n("code",[t._v("Option_f64")]),t._v(",接着将泛型定义替换为这两个具体的定义。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("编译器生成的单态化版本的代码看起来像这样,并包含将泛型 "),s("code",[this._v("Option")]),this._v(" 替换为编译器创建的具体定义后的用例代码:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Option_i32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n None"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("enum")]),t._v(" Option_f64 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f64"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n None"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" integer "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Option_i32"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" float "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Option_f64"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5.0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])}],!1,null,null,null);e.options.__file="ch10-01-syntax.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/53.377c0582.js b/src/.vuepress/dist/assets/js/53.377c0582.js
new file mode 100644
index 0000000..2b3eafe
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/53.377c0582.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[53],{217:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch10-02-traits.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch10-02-traits.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),a("p",[t._v("一个类型的行为由其可供调用的方法构成。如果可以对不同类型调用相同的方法的话,这些类型就可以共享相同的行为了。trait 定义是一种将方法签名组合起来的方法,目的是定义一个实现某些目的所必需的行为的集合。")]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),a("p",[t._v("trait 体中可以有多个方法:一行一个方法签名且都以分号结尾。")]),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),a("p",[t._v("有时为 trait 中的某些或全部方法提供默认的行为,而不是在每个类型的每个实现中都定义自己的行为是很有用的。这样当为某个特定类型实现 trait 时,可以选择保留或重载每个方法的默认行为。")]),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._m(36),t._v(" "),t._m(37),t._m(38),t._v(" "),t._m(39),t._m(40),t._v(" "),a("p",[t._v("注意无法从相同方法的重载实现中调用默认方法。")]),t._v(" "),t._m(41),t._v(" "),a("p",[t._v("知道了如何定义 trait 和在类型上实现这些 trait 之后,我们可以探索一下如何使用 trait 来接受多种不同类型的参数。")]),t._v(" "),t._m(42),t._v(" "),t._m(43),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._m(48),t._v(" "),t._m(49),t._v(" "),t._m(50),t._m(51),t._v(" "),t._m(52),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),a("p",[t._v("这个语法也适用于泛型的 trait bound:")]),t._v(" "),t._m(56),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._m(60),t._v(" "),t._m(61),a("p",[t._v("这个函数签名就显得不那么杂乱,函数名、参数列表和返回值类型都离得很近,看起来类似没有很多 trait bounds 的函数。")]),t._v(" "),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._m(65),t._v(" "),t._m(66),t._v(" "),t._m(67),t._v(" "),t._m(68),t._m(69),t._v(" "),t._m(70),t._v(" "),t._m(71),t._v(" "),t._m(72),t._m(73),t._v(" "),t._m(74),a("p",[t._v("但是如果编译代码的话,会出现一些不同的错误:")]),t._v(" "),t._m(75),t._m(76),t._v(" "),t._m(77),t._v(" "),t._m(78),t._v(" "),t._m(79),t._m(80),t._v(" "),t._m(81),t._v(" "),t._m(82),t._v(" "),t._m(83),t._v(" "),t._m(84),t._v(" "),t._m(85),t._m(86),t._v(" "),t._m(87),t._v(" "),t._m(88),t._m(89),t._v(" "),t._m(90),a("p",[t._v("blanket implementation 会出现在 trait 文档的 “Implementers” 部分。")]),t._v(" "),a("p",[t._v("trait 和 trait bound 让我们使用泛型类型参数来减少重复,并仍然能够向编译器明确指定泛型类型需要拥有哪些行为。因为我们向编译器提供了 trait bound 信息,它就可以检查代码中所用到的具体类型是否提供了正确的行为。在动态类型语言中,如果我们尝试调用一个类型并没有实现的方法,会在运行时出现错误。Rust 将这些错误移动到了编译时,甚至在代码能够运行之前就强迫我们修复错误。另外,我们也无需编写运行时检查行为的代码,因为在编译时就已经检查过了,这样相比其他那些不愿放弃泛型灵活性的语言有更好的性能。")]),t._v(" "),t._m(91)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"trait:定义共享的行为"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#trait:定义共享的行为","aria-hidden":"true"}},[this._v("#")]),this._v(" trait:定义共享的行为")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("em",[this._v("trait")]),this._v(" 告诉 Rust 编译器某个特定类型拥有可能与其他类型共享的功能。可以通过 trait 以一种抽象的方式定义共享的行为。可以使用 "),s("em",[this._v("trait bounds")]),this._v(" 指定泛型是任何拥有特定行为的类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("blockquote",[s("p",[this._v("注意:"),s("em",[this._v("trait")]),this._v(" 类似于其他语言中的常被称为 "),s("strong",[this._v("接口")]),this._v("("),s("em",[this._v("interfaces")]),this._v(")的功能,虽然有一些不同。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"定义-trait"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#定义-trait","aria-hidden":"true"}},[this._v("#")]),this._v(" 定义 trait")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("例如,这里有多个存放了不同类型和属性文本的结构体:结构体 "),s("code",[this._v("NewsArticle")]),this._v(" 用于存放发生于世界各地的新闻故事,而结构体 "),s("code",[this._v("Tweet")]),this._v(" 最多只能存放 280 个字符的内容,以及像是否转推或是否是对推友的回复这样的元数据。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("我们想要创建一个多媒体聚合库用来显示可能储存在 "),a("code",[t._v("NewsArticle")]),t._v(" 或 "),a("code",[t._v("Tweet")]),t._v(" 实例中的数据的总结。每一个结构体都需要的行为是他们是能够被总结的,这样的话就可以调用实例的 "),a("code",[t._v("summarize")]),t._v(" 方法来请求总结。示例 10-12 中展示了一个表现这个概念的 "),a("code",[t._v("Summary")]),t._v(" trait 的定义:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Summary "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("summarize")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-12:"),s("code",[this._v("Summarizable")]),this._v(" trait 定义,它包含由 "),s("code",[this._v("summarize")]),this._v(" 方法提供的行为")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里使用 "),s("code",[this._v("trait")]),this._v(" 关键字来声明一个 trait,后面是 trait 的名字,在这个例子中是 "),s("code",[this._v("Summary")]),this._v("。在大括号中声明描述实现这个 trait 的类型所需要的行为的方法签名,在这个例子中是 "),s("code",[this._v("fn summarize(&self) -> String")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在方法签名后跟分号,而不是在大括号中提供其实现。接着每一个实现这个 trait 的类型都需要提供其自定义行为的方法体,编译器也会确保任何实现 "),s("code",[this._v("Summary")]),this._v(" trait 的类型都拥有与这个签名的定义完全一致的 "),s("code",[this._v("summarize")]),this._v(" 方法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"为类型实现-trait"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#为类型实现-trait","aria-hidden":"true"}},[this._v("#")]),this._v(" 为类型实现 trait")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("现在我们定义了 "),a("code",[t._v("Summary")]),t._v(" trait,接着就可以在多媒体聚合库中需要拥有这个行为的类型上实现它了。示例 10-13 中展示了 "),a("code",[t._v("NewsArticle")]),t._v(" 结构体上 "),a("code",[t._v("Summary")]),t._v(" trait 的一个实现,它使用标题、作者和创建的位置作为 "),a("code",[t._v("summarize")]),t._v(" 的返回值。对于 "),a("code",[t._v("Tweet")]),t._v(" 结构体,我们选择将 "),a("code",[t._v("summarize")]),t._v(" 定义为用户名后跟推文的全部文本作为返回值,并假设推文内容已经被限制为 280 字符以内。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Summary "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("summarize")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" NewsArticle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" headline"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" location"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" author"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" content"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Summary "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" NewsArticle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("summarize")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("format!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}, by {} ({})"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("headline"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("author"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("location"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Tweet "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" username"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" content"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" reply"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" retweet"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Summary "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Tweet "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("summarize")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("format!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"{}: {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("username"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("content"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-13:在 "),s("code",[this._v("NewsArticle")]),this._v(" 和 "),s("code",[this._v("Tweet")]),this._v(" 类型上实现 "),s("code",[this._v("Summary")]),this._v(" trait")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在类型上实现 trait 类似于实现与 trait 无关的方法。区别在于 "),s("code",[this._v("impl")]),this._v(" 关键字之后,我们提供需要实现 trait 的名称,接着是 "),s("code",[this._v("for")]),this._v(" 和需要实现 trait 的类型的名称。在 "),s("code",[this._v("impl")]),this._v(" 块中,使用 trait 定义中的方法签名,不过不再后跟分号,而是需要在大括号中编写函数体来为特定类型实现 trait 方法所拥有的行为。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("一旦实现了 trait,我们就可以用与 "),s("code",[this._v("NewsArticle")]),this._v(" 和 "),s("code",[this._v("Tweet")]),this._v(" 实例的非 trait 方法一样的方式调用 trait 方法了:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let tweet = Tweet {\n username: String::from("horse_ebooks"),\n content: String::from("of course, as you probably already know, people"),\n reply: false,\n retweet: false,\n};\n\nprintln!("1 new tweet: {}", tweet.summarize());\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这会打印出 "),s("code",[this._v("1 new tweet: horse_ebooks: of course, as you probably already know, people")]),this._v("。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("注意因为示例 10-13 中我们在相同的 "),a("em",[t._v("lib.rs")]),t._v(" 里定义了 "),a("code",[t._v("Summary")]),t._v(" trait 和 "),a("code",[t._v("NewsArticle")]),t._v(" 与 "),a("code",[t._v("Tweet")]),t._v(" 类型,所以他们是位于同一作用域的。如果这个 "),a("em",[t._v("lib.rs")]),t._v(" 是对应 "),a("code",[t._v("aggregator")]),t._v(" crate 的,而别人想要利用我们 crate 的功能为其自己的库作用域中的结构体实现 "),a("code",[t._v("Summary")]),t._v(" trait。首先他们需要将 trait 引入作用域。这可以通过指定 "),a("code",[t._v("use aggregator::Summary;")]),t._v(" 实现,这样就可以为其类型实现 "),a("code",[t._v("Summary")]),t._v(" trait 了。"),a("code",[t._v("Summary")]),t._v(" 还必须是公有 trait 使得其他 crate 可以实现它,这也是为什么实例 10-12 中将 "),a("code",[t._v("pub")]),t._v(" 置于 "),a("code",[t._v("trait")]),t._v(" 之前。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("一个实现 trait 时需要注意的限制是只有要么 tait 或者类型是位于 crate 作用域本地时才能为其实现该 trait。例如,可以为像 "),a("code",[t._v("aggregator")]),t._v(" crate 的 "),a("code",[t._v("Tweet")]),t._v(" 这样的自定义类型实现如标准库中的 "),a("code",[t._v("Display")]),t._v(" 这样 trait,因为 "),a("code",[t._v("Tweet")]),t._v(" 类型位于 "),a("code",[t._v("aggregator")]),t._v(" crate 本地。也可以在 "),a("code",[t._v("aggregator")]),t._v(" crate 中为 "),a("code",[t._v("Vec")]),t._v(" 实现 "),a("code",[t._v("Summary")]),t._v(",因为 "),a("code",[t._v("Summary")]),t._v(" trait 位于 "),a("code",[t._v("aggregator")]),t._v(" crate 本地。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("但是不能在外部类型上实现外部 trait。例如,不能在 "),a("code",[t._v("aggregator")]),t._v(" crate 中为 "),a("code",[t._v("Vec")]),t._v(" 实现 "),a("code",[t._v("Display")]),t._v(" trait。因为 "),a("code",[t._v("Display")]),t._v(" 和 "),a("code",[t._v("Vec")]),t._v(" 都定义于标准库并不位于 "),a("code",[t._v("aggregator")]),t._v(" crate 本地。这个限制是被称为 "),a("strong",[t._v("相干性")]),t._v("("),a("em",[t._v("coherence")]),t._v(") 的程序属性的一部分,或者更具体的说是 "),a("strong",[t._v("孤儿规则")]),t._v("("),a("em",[t._v("orphan rule")]),t._v("),其得名于不存在父类型。这条规则确保了其他人编写的代码不会破坏你代码,反之亦然。没有这条规则的话,两个 crate 可以分别对相同类型实现相同的 trait,而Rust 将无从得知应该使用哪一个实现。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"默认实现"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#默认实现","aria-hidden":"true"}},[this._v("#")]),this._v(" 默认实现")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 10-14 中展示了如何为 "),s("code",[this._v("Summary")]),this._v(" trait 的 "),s("code",[this._v("summarize")]),this._v(" 方法指定一个默认的字符串值,而不是像示例 10-12 中那样只是定义方法签名:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Summary "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("summarize")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"(Read more...)"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-14:"),s("code",[this._v("Summary")]),this._v(" trait 的定义,带有一个 "),s("code",[this._v("summarize")]),this._v(" 方法的默认实现")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果想要对 "),s("code",[this._v("NewsArticle")]),this._v(" 实例使用这个默认实现,而不是定义一个自己的实现,则可以通过 impl Summary for NewsArticle {} 指定一个空的 "),s("code",[this._v("impl")]),this._v(" 块。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("即便选择不再直接为 "),a("code",[t._v("NewsArticle")]),t._v(" 定义 "),a("code",[t._v("summarize")]),t._v(" 方法了,因为我们提供了一个默认实现而且 "),a("code",[t._v("NewsArticle")]),t._v(" 被指定为实现了 "),a("code",[t._v("Summary")]),t._v(" trait。为此我们仍然可以像这样对 "),a("code",[t._v("NewsArticle")]),t._v(" 的实例调用 "),a("code",[t._v("summarize")]),t._v(" 方法:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let article = NewsArticle {\n headline: String::from("Penguins win the Stanley Cup Championship!"),\n location: String::from("Pittsburgh, PA, USA"),\n author: String::from("Iceburgh"),\n content: String::from("The Pittsburgh Penguins once again are the best\n hockey team in the NHL."),\n};\n\nprintln!("New article available! {}", article.summarize());\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这段代码会打印 "),s("code",[this._v("New article available! (Read more...)")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为 "),s("code",[this._v("summarize")]),this._v(" 创建默认实现并不要求对示例 10-13 中 "),s("code",[this._v("Tweet")]),this._v(" 上的 "),s("code",[this._v("Summary")]),this._v(" 实现做任何改变。其原因是重载一个默认实现的语法与实现没有默认实现的 trait 方法一样。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("将 "),a("code",[t._v("Summarizable")]),t._v(" trait 改变为拥有默认 "),a("code",[t._v("summary")]),t._v(" 实现并不要求对示例 10-13 中 "),a("code",[t._v("Tweet")]),t._v(" 和示例 10-14 中 "),a("code",[t._v("WeatherForecast")]),t._v(" 的 "),a("code",[t._v("Summarizable")]),t._v(" 实现做任何改变:重载一个默认实现的语法与实现没有默认实现的 trait 方法时完全一样的。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("默认实现允许调用相同 trait 中的其他方法,哪怕这些方法没有默认实现。如此,trait 可以实现很多有用的功能而只需实现一小部分特定内容。我们可以选择让"),a("code",[t._v("Summary")]),t._v(" trait 也拥有一个要求实现的"),a("code",[t._v("summarize_author")]),t._v(" 方法,接着 "),a("code",[t._v("summarize")]),t._v(" 方法则提供默认实现并调用 "),a("code",[t._v("summarize_author")]),t._v(" 方法:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Summary "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("summarize_author")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("summarize")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("format!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"(Read more from {}...)"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("summarize_author")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为了使用这个版本的 "),s("code",[this._v("Summary")]),this._v(",只需在实现 trait 时定义 "),s("code",[this._v("summarize_author")]),this._v(" 即可:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('impl Summary for Tweet {\n fn summarize_author(&self) -> String {\n format!("@{}", self.username)\n }\n}\n')])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("一旦定义了 "),a("code",[t._v("summarize_author")]),t._v(",我们就可以对 "),a("code",[t._v("Tweet")]),t._v(" 结构体的实例调用 "),a("code",[t._v("summarize")]),t._v(" 了,而 "),a("code",[t._v("summary")]),t._v(" 的默认实现会调用我们提供的 "),a("code",[t._v("summarize_author")]),t._v(" 定义。因为实现了 "),a("code",[t._v("summarize_author")]),t._v(","),a("code",[t._v("Summary")]),t._v(" trait 就提供了 "),a("code",[t._v("summarize")]),t._v(" 方法的功能,且无需编写更多的代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let tweet = Tweet {\n username: String::from("horse_ebooks"),\n content: String::from("of course, as you probably already know, people"),\n reply: false,\n retweet: false,\n};\n\nprintln!("1 new tweet: {}", tweet.summarize());\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这会打印出 "),s("code",[this._v("1 new tweet: (Read more from @horse_ebooks...)")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"trait-作为参数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#trait-作为参数","aria-hidden":"true"}},[this._v("#")]),this._v(" trait 作为参数")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("例如在示例 10-13 中为 "),a("code",[t._v("NewsArticle")]),t._v(" 和 "),a("code",[t._v("Tweet")]),t._v(" 类型实现了 "),a("code",[t._v("Summary")]),t._v(" trait。我们可以定义一个函数 "),a("code",[t._v("notify")]),t._v(" 来调用其参数 "),a("code",[t._v("item")]),t._v(" 上的 "),a("code",[t._v("summarize")]),t._v(" 方法,该参数为一些实现了 "),a("code",[t._v("Summary")]),t._v(" trait 的方法。为此可以使用 ‘"),a("code",[t._v("impl Trait")]),t._v("’ 语法,像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('pub fn notify(item: impl Summary) {\n println!("Breaking news! {}", item.summarize());\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 "),s("code",[this._v("notify")]),this._v(" 函数体中,可以调用任何来自 "),s("code",[this._v("Summary")]),this._v(" trait 的方法,比如 "),s("code",[this._v("summarize")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"trait-bounds"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#trait-bounds","aria-hidden":"true"}},[this._v("#")]),this._v(" Trait Bounds")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("impl Trait")]),this._v(" 语法适用于短小的例子,它不过是一个较长形式的语法糖。这被称为 "),s("em",[this._v("trait bound")]),this._v(",这看起来像:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('pub fn notify(item: T) {\n println!("Breaking news! {}", item.summarize());\n}\n')])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这与之前的例子相同,不过稍微冗长了一些。trait bound 与泛型参数声明在一起,位于尖括号中分号的后面。因为 "),a("code",[t._v("T")]),t._v(" 的 trait bound,我们可以传递任何 "),a("code",[t._v("NewsArticle")]),t._v(" 或 "),a("code",[t._v("Tweet")]),t._v(" 的实例调用 "),a("code",[t._v("notify")]),t._v("。用任何其他类型,比如 "),a("code",[t._v("String")]),t._v(" 或 "),a("code",[t._v("i32")]),t._v(",调用该函数的代码将不能编译,因为这些类型没有实现 "),a("code",[t._v("Summary")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("何时应该使用这种形式而不是 "),s("code",[this._v("impl Trait")]),this._v(" 呢?虽然 "),s("code",[this._v("impl Trait")]),this._v(" 适用于短小的例子,trait bound 则适用于更复杂的场景。例如,比如需要获取两个实现了 "),s("code",[this._v("Summary")]),this._v(" 的类型:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("pub fn notify(item1: impl Summary, item2: impl Summary) {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这适用于 "),s("code",[this._v("item1")]),this._v(" 和 "),s("code",[this._v("item2")]),this._v(" 允许是不同类型的情况(只要它们都实现了 "),s("code",[this._v("Summary")]),this._v(")。不过如果你希望强制它们都是相同类型呢?这只有在使用 trait bound 时才有可能:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("pub fn notify(item1: T, item2: T) {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"通过-指定多个-trait"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#通过-指定多个-trait","aria-hidden":"true"}},[this._v("#")]),this._v(" 通过 "),s("code",[this._v("+")]),this._v(" 指定多个 trait")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("如果 "),a("code",[t._v("notify")]),t._v(" 需要显示 "),a("code",[t._v("item")]),t._v(" 的格式化形式,同时也要使用 "),a("code",[t._v("summarize")]),t._v(" 方法,那么 "),a("code",[t._v("item")]),t._v(" 就需要同时实现两个不同的 trait:"),a("code",[t._v("Display")]),t._v(" 和 "),a("code",[t._v("Summary")]),t._v("。这可以通过 "),a("code",[t._v("+")]),t._v(" 语法实现:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("pub fn notify(item: impl Summary + Display) {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("pub fn notify(item: T) {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"通过-where-简化代码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#通过-where-简化代码","aria-hidden":"true"}},[this._v("#")]),this._v(" 通过 "),s("code",[this._v("where")]),this._v(" 简化代码")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("然而,使用过多的 trait bound 也有缺点。每个泛型有其自己的 trait bound,所以有多个泛型参数的函数在名称和参数列表之间会有很长的 trait bound 信息,这使得函数签名难以阅读。为此,Rust 有另一个在函数签名之后的 "),s("code",[this._v("where")]),this._v(" 从句中指定 trait bound 的语法。所以除了这么写:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn some_function(t: T, u: U) -> i32 {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("还可以像这样使用 "),s("code",[this._v("where")]),this._v(" 从句:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn some_function(t: T, u: U) -> i32\n where T: Display + Clone,\n U: Clone + Debug\n{\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"返回-trait"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#返回-trait","aria-hidden":"true"}},[this._v("#")]),this._v(" 返回 trait")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("也可以在返回值中使用 "),s("code",[this._v("impl Trait")]),this._v(" 语法,来返回实现了某个 trait 的类型:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn returns_summarizable() -> impl Summary {\n Tweet {\n username: String::from("horse_ebooks"),\n content: String::from("of course, as you probably already know, people"),\n reply: false,\n retweet: false,\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个签名表明,“我要返回某个实现了 "),s("code",[this._v("Summary")]),this._v(" trait 的类型,但是不确定其具体的类型”。在例子中返回了一个 "),s("code",[this._v("Tweet")]),this._v(",不过调用方并不知情。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这有什么用呢?在第十三章中,我们会学些两个大量依赖 trait 的功能:闭包和迭代器。这些功能创建只有编译器知道的类型,或者是非常非常长的类型。"),s("code",[this._v("impl Trait")]),this._v(" 允许你简单的说 “返回一个 "),s("code",[this._v("Iterator")]),this._v("” 而无需写出实际的冗长的类型。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不过这只适用于返回单一类型的情况。例如,这样就 "),s("strong",[this._v("不行")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn returns_summarizable(switch: bool) -> impl Summary {\n if switch {\n NewsArticle {\n headline: String::from("Penguins win the Stanley Cup Championship!"),\n location: String::from("Pittsburgh, PA, USA"),\n author: String::from("Iceburgh"),\n content: String::from("The Pittsburgh Penguins once again are the best\n hockey team in the NHL."),\n }\n } else {\n Tweet {\n username: String::from("horse_ebooks"),\n content: String::from("of course, as you probably already know, people"),\n reply: false,\n retweet: false,\n }\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里尝试返回 "),s("code",[this._v("NewsArticle")]),this._v(" 或 "),s("code",[this._v("Tweet")]),this._v("。这不能编译,因为 "),s("code",[this._v("impl Trait")]),this._v(" 工作方式的限制。为了编写这样的代码,你不得不等到第十七章的 “为使用不同类型的值而设计的 trait 对象” 部分。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-trait-bounds-来修复-largest-函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-trait-bounds-来修复-largest-函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 trait bounds 来修复 "),s("code",[this._v("largest")]),this._v(" 函数")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在你知道了如何使用泛型参数 trait bound 来指定所需的行为。让我们回到实例 10-5 修复使用泛型类型参数的 "),s("code",[this._v("largest")]),this._v(" 函数定义!最后尝试代时出现的错误是:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0369]: binary operation `>` cannot be applied to type `T`\n --\x3e src/main.rs:5:12\n |\n5 | if item > largest {\n | ^^^^^^^^^^^^^^\n |\n = note: an implementation of `std::cmp::PartialOrd` might be missing for `T`\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("在 "),a("code",[t._v("largest")]),t._v(" 函数体中我们想要使用大于运算符("),a("code",[t._v(">")]),t._v(")比较两个 "),a("code",[t._v("T")]),t._v(" 类型的值。这个运算符被定义为标准库中 trait "),a("code",[t._v("std::cmp::PartialOrd")]),t._v(" 的一个默认方法。所以需要在 "),a("code",[t._v("T")]),t._v(" 的 trait bound 中指定 "),a("code",[t._v("PartialOrd")]),t._v(",这样 "),a("code",[t._v("largest")]),t._v(" 函数可以用于任何可以比较大小的类型的 slice。因为 "),a("code",[t._v("PartialOrd")]),t._v(" 位于 prelude 中所以并不需要手动将其引入作用域。将 "),a("code",[t._v("largest")]),t._v(" 的签名修改为如下:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn largest(list: &[T]) -> T {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0508]: cannot move out of type `[T]`, a non-copy slice\n --\x3e src/main.rs:2:23\n |\n2 | let mut largest = list[0];\n | ^^^^^^^\n | |\n | cannot move out of here\n | help: consider using a reference instead: `&list[0]`\n\nerror[E0507]: cannot move out of borrowed content\n --\x3e src/main.rs:4:9\n |\n4 | for &item in list.iter() {\n | ^----\n | ||\n | |hint: to prevent move, use `ref item` or `ref mut item`\n | cannot move out of borrowed content\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("错误的核心是 "),a("code",[t._v("cannot move out of type [T], a non-copy slice")]),t._v(",对于非泛型版本的 "),a("code",[t._v("largest")]),t._v(" 函数,我们只尝试了寻找最大的 "),a("code",[t._v("i32")]),t._v(" 和 "),a("code",[t._v("char")]),t._v("。正如第四章 “只在栈上的数据:拷贝” 部分讨论过的,像 "),a("code",[t._v("i32")]),t._v(" 和 "),a("code",[t._v("char")]),t._v(" 这样的类型是已知大小的并可以储存在栈上,所以他们实现了 "),a("code",[t._v("Copy")]),t._v(" trait。当我们将 "),a("code",[t._v("largest")]),t._v(" 函数改成使用泛型后,现在 "),a("code",[t._v("list")]),t._v(" 参数的类型就有可能是没有实现 "),a("code",[t._v("Copy")]),t._v(" trait 的。这意味着我们可能不能将 "),a("code",[t._v("list[0]")]),t._v(" 的值移动到 "),a("code",[t._v("largest")]),t._v(" 变量中,这导致了上面的错误。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("为了只对实现了 "),a("code",[t._v("Copy")]),t._v(" 的类型调用这些代码,可以在 "),a("code",[t._v("T")]),t._v(" 的 trait bounds 中增加 "),a("code",[t._v("Copy")]),t._v("!示例 10-15 中展示了一个可以编译的泛型版本的 "),a("code",[t._v("largest")]),t._v(" 函数的完整代码,只要传递给 "),a("code",[t._v("largest")]),t._v(" 的 slice 值的类型实现了 "),a("code",[t._v("PartialOrd")]),t._v(" "),a("strong",[t._v("和")]),t._v(" "),a("code",[t._v("Copy")]),t._v(" 这两个 trait,例如 "),a("code",[t._v("i32")]),t._v(" 和 "),a("code",[t._v("char")]),t._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" largest"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" PartialOrd "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" Copy"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("list"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" T "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" largest "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" list"),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("0")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("item "),a("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" list"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("iter")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" item "),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" largest "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n largest "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" item"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n largest\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" number_list "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token number"}},[t._v("34")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("50")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("25")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("100")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("65")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" result "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("largest")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("number_list"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"The largest number is {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" result"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" char_list "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("vec!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{attrs:{class:"token char string"}},[t._v("'y'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token char string"}},[t._v("'m'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token char string"}},[t._v("'a'")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token char string"}},[t._v("'q'")]),a("span",{attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" result "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("largest")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("char_list"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"The largest char is {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" result"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-15:一个可以用于任何实现了 "),s("code",[this._v("PartialOrd")]),this._v(" 和 "),s("code",[this._v("Copy")]),this._v(" trait 的泛型的 "),s("code",[this._v("largest")]),this._v(" 函数")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("如果并不希望限制 "),a("code",[t._v("largest")]),t._v(" 函数只能用于实现了 "),a("code",[t._v("Copy")]),t._v(" trait 的类型,我们可以在 "),a("code",[t._v("T")]),t._v(" 的 trait bounds 中指定 "),a("code",[t._v("Clone")]),t._v(" 而不是 "),a("code",[t._v("Copy")]),t._v("。并克隆 slice 的每一个值使得 "),a("code",[t._v("largest")]),t._v(" 函数拥有其所有权。使用 "),a("code",[t._v("clone")]),t._v(" 函数意味着对于类似 "),a("code",[t._v("String")]),t._v(" 这样拥有堆上数据的类型,会潜在的分配更多堆上空间,而堆分配在涉及大量数据时可能会相当缓慢。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("另一种 "),a("code",[t._v("largest")]),t._v(" 的实现方式是返回在 slice 中 "),a("code",[t._v("T")]),t._v(" 值的引用。如果我们将函数返回值从 "),a("code",[t._v("T")]),t._v(" 改为 "),a("code",[t._v("&T")]),t._v(" 并改变函数体使其能够返回一个引用,我们将不需要任何 "),a("code",[t._v("Clone")]),t._v(" 或 "),a("code",[t._v("Copy")]),t._v(" 的 trait bounds 而且也不会有任何的堆分配。尝试自己实现这种替代解决方式吧!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-trait-bound-有条件的实现方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-trait-bound-有条件的实现方法","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 trait bound 有条件的实现方法")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("通过使用带有 trait bound 的泛型参数的 "),a("code",[t._v("impl")]),t._v(" 块,可以有条件的只为实现了特定 trait 的类型实现方法。例如,示例 10-16 中的类型 "),a("code",[t._v("Pair")]),t._v(" 总是实现了 "),a("code",[t._v("new")]),t._v(" 方法,不过只有 "),a("code",[t._v("Pair")]),t._v(" 内部的 "),a("code",[t._v("T")]),t._v(" 类型实现了 "),a("code",[t._v("PartialOrd")]),t._v(" trait 来允许比较 "),a("strong",[t._v("和")]),t._v(" "),a("code",[t._v("Display")]),t._v(" trait 来启用打印,才会实现 "),a("code",[t._v("cmp_display")]),t._v(" 方法:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fmt"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Display"),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Pair"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" Pair"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Self "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Self "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n y"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Display "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" PartialOrd"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" Pair"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("cmp_display")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x "),a("span",{attrs:{class:"token operator"}},[t._v(">=")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("y "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"The largest member is x = {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("x"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("println!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"The largest member is y = {}"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("y"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-17:根据 trait bound 在泛型上有条件的实现方法")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("也可以对任何实现了特定 trait 的类型有条件的实现 trait。对任何满足特定 trait bound 的类型实现 trait 被称为 "),a("em",[t._v("blanket implementations")]),t._v(",他们被广泛的用于 Rust 标准库中。例如,标准库为任何实现了 "),a("code",[t._v("Display")]),t._v(" trait 的类型实现了 "),a("code",[t._v("ToString")]),t._v(" trait。这个 "),a("code",[t._v("impl")]),t._v(" 块看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("impl ToString for T {\n // --snip--\n}\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("因为标准库有了这些 blanket implementation,我们可以对任何实现了 "),a("code",[t._v("Display")]),t._v(" trait 的类型调用由 "),a("code",[t._v("ToString")]),t._v(" 定义的 "),a("code",[t._v("to_string")]),t._v(" 方法。例如,可以将整型转换为对应的 "),a("code",[t._v("String")]),t._v(" 值,因为整型实现了 "),a("code",[t._v("Display")]),t._v(":")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("to_string")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里还有一种泛型,我们一直在使用它甚至都没有察觉它的存在,这就是 "),s("strong",[this._v("生命周期")]),this._v("("),s("em",[this._v("lifetimes")]),this._v(")。不同于其他泛型帮助我们确保类型拥有期望的行为,生命周期则有助于确保引用在我们需要他们的时候一直有效。让我们学习生命周期是如何做到这些的。")])}],!1,null,null,null);e.options.__file="ch10-02-traits.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/54.8d06b2c9.js b/src/.vuepress/dist/assets/js/54.8d06b2c9.js
new file mode 100644
index 0000000..4632d12
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/54.8d06b2c9.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[54],{216:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch10-03-lifetime-syntax.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch10-03-lifetime-syntax.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),n("p",[t._v("生命周期的概念某种程度上说不同于其他语言中类似的工具,毫无疑问这是 Rust 最与众不同的功能。虽然本章不可能涉及到它全部的内容,我们会讲到一些通常你可能会遇到的生命周期语法以便你熟悉这个概念。查看第十九章 “高级生命周期” 部分了解更多的细节。")]),t._v(" "),t._m(2),t._v(" "),n("p",[t._v("生命周期的主要目标是避免悬垂引用,它会导致程序引用了非预期引用的数据。考虑一下示例 10-17 中的程序,它有一个外部作用域和一个内部作用域.")]),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._v(" "),n("p",[t._v("让我们看看示例 10-19 中这个并没有产生悬垂引用且可以正确编译的例子:")]),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._v(" "),n("p",[t._v("现在我们已经在一个具体的例子中展示了引用的生命周期位于何处,并讨论了 Rust 如何分析生命周期来保证引用总是有效的,接下来让我们聊聊在函数的上下文中参数和返回值的泛型生命周期。")]),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._m(21),t._v(" "),n("p",[t._v("参考之前第四章中的 “字符串 slice 作为参数” 部分中更多关于为什么上面例子中的参数正符合我们期望的讨论。")]),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._m(25),t._v(" "),n("p",[t._v("相应地会出现如下有关生命周期的错误:")]),t._v(" "),t._m(26),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),n("p",[t._v("生命周期注解并不改变任何引用的生命周期的长短。与当函数签名中指定了泛型类型参数后就可以接受任何类型一样,当指定了泛型生命周期后函数也能接受任何生命周期的引用。生命周期注解描述了多个引用生命周期相互的关系,而不影响其生命周期。")]),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._v(" "),n("p",[t._v("当在函数中使用生命周期注解时,这些注解出现在函数签名中,而不存在于函数体中的任何代码中。这是因为 Rust 能够分析函数中代码而不需要任何协助,不过当函数引用或被函数之外的代码引用时,让 Rust 自身分析出参数或返回值的生命周期几乎是不可能的。这些生命周期在每次函数被调用时都可能不同。这也就是为什么我们需要手动标记生命周期。")]),t._v(" "),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._m(50),t._v(" "),n("p",[t._v("如果尝试编译会出现如下错误:")]),t._v(" "),t._m(51),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),t._v(" "),t._m(56),t._v(" "),t._m(57),t._v(" "),t._m(58),t._m(59),t._v(" "),t._m(60),t._v(" "),t._m(61),t._v(" "),t._m(62),t._m(63),t._v(" "),t._m(64),t._m(65),t._v(" "),n("p",[t._v("从结果上看,生命周期语法是关于如何联系函数不同参数和返回值的生命周期的。一旦他们形成了某种联系,Rust 就有了足够的信息来允许内存安全的操作并阻止会产生悬垂指针亦或是违反内存安全的行为。")]),t._v(" "),t._m(66),t._v(" "),t._m(67),t._v(" "),t._m(68),t._v(" "),t._m(69),t._m(70),t._v(" "),t._m(71),t._v(" "),t._m(72),t._v(" "),t._m(73),t._v(" "),n("p",[t._v("在这一部分,我们知道了每一个引用都有一个生命周期,而且需要为使用了引用的函数或结构体指定生命周期。然而,第四章的示例 4-9 中有一个函数,我们在示例 10-26 中再次展示出来,它没有生命周期注解却能成功编译:")]),t._v(" "),t._m(74),t._v(" "),t._m(75),t._m(76),t._v(" "),n("p",[t._v("这个函数没有生命周期注解却能编译是由于一些历史原因:在早期版本(pre-1.0)的 Rust 中,这的确是不能编译的。每一个引用都必须有明确的生命周期。那时的函数签名将会写成这样:")]),t._v(" "),t._m(77),n("p",[t._v("在编写了很多 Rust 代码后,Rust 团队发现在特定情况下 Rust 程序员们总是重复地编写一模一样的生命周期注解。这些场景是可预测的并且遵循几个明确的模式。接着 Rust 团队就把这些模式编码进了 Rust 编译器中,如此借用检查器在这些情况下就能推断出生命周期而不再强制程序员显式的增加注解。")]),t._v(" "),n("p",[t._v("这里我们提到一些 Rust 的历史是因为更多的明确的模式被合并和添加到编译器中是完全可能的。未来只会需要更少的生命周期注解。")]),t._v(" "),t._m(78),t._v(" "),n("p",[t._v("省略规则并不提供完整的推断:如果 Rust 在明确遵守这些规则的前提下变量的生命周期仍然是模棱两可的话,它不会猜测剩余引用的生命周期应该是什么。在这种情况,编译器会给出一个错误,这可以通过增加对应引用之间相联系的生命周期注解来解决。")]),t._v(" "),t._m(79),t._v(" "),n("p",[t._v("编译器采用三条规则来判断引用何时不需要明确的注解。第一条规则适用于输入生命周期,后两条规则适用于输出生命周期。如果编译器检查完这三条规则后仍然存在没有计算出生命周期的引用,编译器将会停止并生成错误。")]),t._v(" "),t._m(80),t._v(" "),t._m(81),t._v(" "),t._m(82),t._v(" "),t._m(83),t._v(" "),t._m(84),t._v(" "),t._m(85),t._m(86),t._v(" "),t._m(87),n("p",[t._v("对于第二条规则,因为这里正好只有一个输入生命周期参数所以是适用的。第二条规则表明输入参数的生命周期将被赋予输出生命周期参数,所以现在签名看起来像这样:")]),t._v(" "),t._m(88),n("p",[t._v("现在这个函数签名中的所有引用都有了生命周期,如此编译器可以继续它的分析而无须程序员标记这个函数签名中的生命周期。")]),t._v(" "),t._m(89),t._v(" "),t._m(90),n("p",[t._v("再次假设我们自己就是编译器并应用第一条规则:每个引用参数都有其自己的生命周期。这次有两个参数,所以就有两个(不同的)生命周期:")]),t._v(" "),t._m(91),t._m(92),t._v(" "),n("p",[t._v("因为第三条规则真正能够适用的就只有方法签名,现在就让我们看看那种情况中的生命周期,并看看为什么这条规则意味着我们经常不需要在方法签名中标注生命周期。")]),t._v(" "),t._m(93),t._v(" "),n("p",[t._v("当为带有生命周期的结构体实现方法时,其语法依然类似示例 10-11 中展示的泛型类型参数的语法。声明和使用生命周期参数的位置依赖于生命周期参数是否同结构体字段或方法参数和返回值相关。")]),t._v(" "),t._m(94),t._v(" "),t._m(95),t._v(" "),t._m(96),t._v(" "),t._m(97),t._m(98),t._v(" "),n("p",[t._v("这里是一个适用于第三条生命周期省略规则的例子:")]),t._v(" "),t._m(99),t._m(100),t._v(" "),t._m(101),t._v(" "),t._m(102),t._v(" "),t._m(103),t._m(104),t._v(" "),t._m(105),t._v(" "),t._m(106),t._v(" "),n("p",[t._v("让我们简要的看一下在同一函数中指定泛型类型参数、trait bounds 和生命周期的语法!")]),t._v(" "),t._m(107),t._m(108),t._v(" "),t._m(109),t._v(" "),n("p",[t._v("这一章介绍了很多的内容!现在你知道了泛型类型参数、trait 和 trait bounds 以及泛型生命周期类型,你已经准备好编写既不重复又能适用于多种场景的代码了。泛型类型参数意味着代码可以适用于不同的类型。trait 和 trait bounds 保证了即使类型是泛型的,这些类型也会拥有所需要的行为。由生命周期注解所指定的引用生命周期之间的关系保证了这些灵活多变的代码不会出现悬垂引用。而所有的这一切发生在编译时所以不会影响运行时效率!")]),t._v(" "),n("p",[t._v("你可能不会相信,这个领域还有更多需要学习的内容:第十七章会讨论 trait 对象,这是另一种使用 trait 的方式。第十九章会涉及到生命周期注解更复杂的场景。第二十章讲解一些高级的类型系统功能。不过接下来,让我们聊聊如何在 Rust 中编写测试,来确保代码的所有功能能像我们希望的那样工作!")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"生命周期与引用有效性"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#生命周期与引用有效性","aria-hidden":"true"}},[this._v("#")]),this._v(" 生命周期与引用有效性")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当在第四章讨论 “引用和借用” 部分时,我们遗漏了一个重要的细节:Rust 中的每一个引用都有其 "),s("strong",[this._v("生命周期")]),this._v("("),s("em",[this._v("lifetime")]),this._v("),也就是引用保持有效的作用域。大部分时候生命周期是隐含并可以推断的,正如大部分时候类型也是可以推断的一样。类似于当因为有多种可能类型的时候必须注明类型,也会出现引用的生命周期以一些不同方式相关联的情况,所以 Rust 需要我们使用泛型生命周期参数来注明他们的关系,这样就能确保运行时实际使用的引用绝对是有效的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"生命周期避免了悬垂引用"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#生命周期避免了悬垂引用","aria-hidden":"true"}},[this._v("#")]),this._v(" 生命周期避免了悬垂引用")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('{\n let r;\n\n {\n let x = 5;\n r = &x;\n }\n\n println!("r: {}", r);\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-17:尝试使用离开作用域的值的引用")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("blockquote",[s("p",[this._v("注意:示例 10-17、10-18 和 10-24 中声明了没有初始值的变量,所以这些变量存在于外部作用域。这乍看之下好像和 Rust 不允许存在空值相冲突。然而如果尝试在给它一个值之前使用这个变量,会出现一个编译时错误,这就说明了 Rust 确实不允许空值。")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("外部作用域声明了一个没有初值的变量 "),n("code",[t._v("r")]),t._v(",而内部作用域声明了一个初值为 5 的变量"),n("code",[t._v("x")]),t._v("。在内部作用域中,我们尝试将 "),n("code",[t._v("r")]),t._v(" 的值设置为一个 "),n("code",[t._v("x")]),t._v(" 的引用。接着在内部作用域结束后,尝试打印出 "),n("code",[t._v("r")]),t._v(" 的值。这段代码不能编译因为 "),n("code",[t._v("r")]),t._v(" 引用的值在尝试使用之前就离开了作用域。如下是错误信息:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0597]: `x` does not live long enough\n --\x3e src/main.rs:7:5\n |\n6 | r = &x;\n | - borrow occurs here\n7 | }\n | ^ `x` dropped here while still borrowed\n...\n10 | }\n | - borrowed value needs to live until here\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("变量 "),n("code",[t._v("x")]),t._v(" 并没有 “存在的足够久”。其原因是 "),n("code",[t._v("x")]),t._v(" 在到达第 7 行内部作用域结束时就离开了作用域。不过 "),n("code",[t._v("r")]),t._v(" 在外部作用域仍是有效的;作用域越大我们就说它 “存在的越久”。如果 Rust 允许这段代码工作,"),n("code",[t._v("r")]),t._v(" 将会引用在 "),n("code",[t._v("x")]),t._v(" 离开作用域时被释放的内存,这时尝试对 "),n("code",[t._v("r")]),t._v(" 做任何操作都不能正常工作。那么 Rust 是如何决定这段代码是不被允许的呢?这得益于借用检查器。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"借用检查器"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#借用检查器","aria-hidden":"true"}},[this._v("#")]),this._v(" 借用检查器")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 编译器有一个 "),s("strong",[this._v("借用检查器")]),this._v("("),s("em",[this._v("borrow checker")]),this._v("),它比较作用域来确保所有的借用都是有效的。示例 10-18 展示了与示例 10-17 相同的例子不过带有变量生命周期的注释:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("{\n let r; // ---------+-- 'a\n // |\n { // |\n let x = 5; // -+-- 'b |\n r = &x; // | |\n } // -+ |\n // |\n println!(\"r: {}\", r); // |\n} // ---------+\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("span",{staticClass:"caption"},[t._v("示例 10-18:"),n("code",[t._v("r")]),t._v(" 和 "),n("code",[t._v("x")]),t._v(" 的生命周期注解,分别叫做 "),n("code",[t._v("'a")]),t._v(" 和 "),n("code",[t._v("'b")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里将 "),n("code",[t._v("r")]),t._v(" 的生命周期标记为 "),n("code",[t._v("'a")]),t._v(" 并将 "),n("code",[t._v("x")]),t._v(" 的生命周期标记为 "),n("code",[t._v("'b")]),t._v("。如你所见,内部的 "),n("code",[t._v("'b")]),t._v(" 块要比外部的生命周期 "),n("code",[t._v("'a")]),t._v(" 小得多。在编译时,Rust 比较这两个生命周期的大小,并发现 "),n("code",[t._v("r")]),t._v(" 拥有生命周期 "),n("code",[t._v("'a")]),t._v(",不过它引用了一个拥有生命周期 "),n("code",[t._v("'b")]),t._v(" 的对象。程序被拒绝编译,因为生命周期 "),n("code",[t._v("'b")]),t._v(" 比生命周期 "),n("code",[t._v("'a")]),t._v(" 要小:被引用的对象比它的引用者存在的时间更短。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// ----------+-- 'b")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// |")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" r "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// --+-- 'a |")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// | |")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"r: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" r"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// | |")]),t._v("\n "),n("span",{attrs:{class:"token comment"}},[t._v("// --+ |")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token comment"}},[t._v("// ----------+")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-19:一个有效的引用,因为数据比引用有着更长的生命周期")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里 "),n("code",[t._v("x")]),t._v(" 拥有生命周期 "),n("code",[t._v("'b")]),t._v(",比 "),n("code",[t._v("'a")]),t._v(" 要大。这就意味着 "),n("code",[t._v("r")]),t._v(" 可以引用 "),n("code",[t._v("x")]),t._v(":Rust 知道 "),n("code",[t._v("r")]),t._v(" 中的引用在 "),n("code",[t._v("x")]),t._v(" 有效的时候也总是有效的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"函数中的泛型生命周期"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#函数中的泛型生命周期","aria-hidden":"true"}},[this._v("#")]),this._v(" 函数中的泛型生命周期")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们来编写一个返回两个字符串 slice 中较长者的函数。这个函数获取两个字符串 slice 并返回一个字符串 slice。一旦我们实现了 "),s("code",[this._v("longest")]),this._v(" 函数,示例 10-20 中的代码应该会打印出 "),s("code",[this._v("The longest string is abcd")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let string1 = String::from("abcd");\n let string2 = "xyz";\n\n let result = longest(string1.as_str(), string2);\n println!("The longest string is {}", result);\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-20:"),s("code",[this._v("main")]),this._v(" 函数调用 "),s("code",[this._v("longest")]),this._v(" 函数来寻找两个字符串 slice 中较长的一个")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果尝试像示例 10-21 中那样实现 "),s("code",[this._v("longest")]),this._v(" 函数,它并不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn longest(x: &str, y: &str) -> &str {\n if x.len() > y.len() {\n x\n } else {\n y\n }\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-21:一个 "),s("code",[this._v("longest")]),this._v(" 函数的实现,它返回两个字符串 slice 中较长者,现在还不能编译")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0106]: missing lifetime specifier\n --\x3e src/main.rs:1:33\n |\n1 | fn longest(x: &str, y: &str) -> &str {\n | ^ expected lifetime parameter\n |\n = help: this function's return type contains a borrowed value, but the\nsignature does not say whether it is borrowed from `x` or `y`\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("提示文本揭示了返回值需要一个泛型生命周期参数,因为 Rust 并不知道将要返回的引用是指向 "),n("code",[t._v("x")]),t._v(" 或 "),n("code",[t._v("y")]),t._v("。事实上我们也不知道,因为函数体中 "),n("code",[t._v("if")]),t._v(" 块返回一个 "),n("code",[t._v("x")]),t._v(" 的引用而 "),n("code",[t._v("else")]),t._v(" 块返回一个 "),n("code",[t._v("y")]),t._v(" 的引用!")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当我们定义这个函数的时候,并不知道传递给函数的具体值,所以也不知道到底是 "),n("code",[t._v("if")]),t._v(" 还是 "),n("code",[t._v("else")]),t._v(" 会被执行。我们也不知道传入的引用的具体生命周期,所以也就不能像示例 10-18 和 10-19 那样通过观察作用域来确定返回的引用是否总是有效。借用检查器自身同样也无法确定,因为它不知道 "),n("code",[t._v("x")]),t._v(" 和 "),n("code",[t._v("y")]),t._v(" 的生命周期是如何与返回值的生命周期相关联的。为了修复这个错误,我们将增加泛型生命周期参数来定义引用间的关系以便借用检查器可以进行分析。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"生命周期注解语法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#生命周期注解语法","aria-hidden":"true"}},[this._v("#")]),this._v(" 生命周期注解语法")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("生命周期注解有着一个不太常见的语法:生命周期参数名称必须以撇号("),s("code",[this._v("'")]),this._v(")开头,其名称通常全是小写,类似于泛型其名称非常短。"),s("code",[this._v("'a")]),this._v(" 是大多数人默认使用的名称。生命周期参数注解位于引用的 "),s("code",[this._v("&")]),this._v(" 之后,并有一个空格来将引用类型与生命周期注解分隔开。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里有一些例子:我们有一个没有生命周期参数的 "),n("code",[t._v("i32")]),t._v(" 的引用,一个有叫做 "),n("code",[t._v("'a")]),t._v(" 的生命周期参数的 "),n("code",[t._v("i32")]),t._v(" 的引用,和一个生命周期也是 "),n("code",[t._v("'a")]),t._v(" 的 "),n("code",[t._v("i32")]),t._v(" 的可变引用:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("&i32 // 引用\n&'a i32 // 带有显式生命周期的引用\n&'a mut i32 // 带有显式生命周期的可变引用\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("单个的生命周期注解本身没有多少意义,因为生命周期注解告诉 Rust 多个引用的泛型生命周期参数如何相互联系的。例如如果函数有一个生命周期 "),n("code",[t._v("'a")]),t._v(" 的 "),n("code",[t._v("i32")]),t._v(" 的引用的参数 "),n("code",[t._v("first")]),t._v("。还有另一个同样是生命周期 "),n("code",[t._v("'a")]),t._v(" 的 "),n("code",[t._v("i32")]),t._v(" 的引用的参数 "),n("code",[t._v("second")]),t._v("。这两个生命周期注解意味着引用 "),n("code",[t._v("first")]),t._v(" 和 "),n("code",[t._v("second")]),t._v(" 必须与这泛型生命周期存在得一样久。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"函数签名中的生命周期注解"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#函数签名中的生命周期注解","aria-hidden":"true"}},[this._v("#")]),this._v(" 函数签名中的生命周期注解")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在来看看 "),s("code",[this._v("longest")]),this._v(" 函数的上下文中的生命周期。就像泛型类型参数,泛型生命周期参数需要声明在函数名和参数列表间的尖括号中。这里我们想要告诉 Rust 关于参数中的引用和返回值之间的限制是他们都必须拥有相同的生命周期,就像示例 10-22 中在每个引用中都加上了 "),s("code",[this._v("'a")]),this._v(" 那样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" longest"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n y\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-22:"),s("code",[this._v("longest")]),this._v(" 函数定义指定了签名中所有的引用必须有相同的生命周期 "),s("code",[this._v("'a")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这段代码能够编译并会产生我们希望得到的示例 10-20 中的 "),s("code",[this._v("main")]),this._v(" 函数的结果。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("现在函数签名表明对于某些生命周期 "),n("code",[t._v("'a")]),t._v(",函数会获取两个参数,他们都是与生命周期 "),n("code",[t._v("'a")]),t._v(" 存在的一样长的字符串 slice。函数会返回一个同样也与生命周期 "),n("code",[t._v("'a")]),t._v(" 存在的一样长的字符串 slice。这就是我们告诉 Rust 需要其保证的契约。记住通过在函数签名中指定生命周期参数时,我们并没有改变任何传入后返回的值的生命周期。而是指出任何不遵守这个协议的传入值都将被借用检查器拒绝。注意 "),n("code",[t._v("longest")]),t._v(" 函数并不需要知道 "),n("code",[t._v("x")]),t._v(" 和 "),n("code",[t._v("y")]),t._v(" 具体会存在多久,而只需要知道有某个可以被 "),n("code",[t._v("'a")]),t._v(" 替代的作用域将会满足这个签名。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当具体的引用被传递给 "),n("code",[t._v("longest")]),t._v(" 时,被 "),n("code",[t._v("'a")]),t._v(" 所替代的具体生命周期是 "),n("code",[t._v("x")]),t._v(" 的作用域与 "),n("code",[t._v("y")]),t._v(" 的作用域相重叠的那一部分。换一种说法就是泛型生命周期 "),n("code",[t._v("'a")]),t._v(" 的具体生命周期等同于 "),n("code",[t._v("x")]),t._v(" 和 "),n("code",[t._v("y")]),t._v(" 的生命周期中较小的那一个。因为我们用相同的生命周期参数 "),n("code",[t._v("'a")]),t._v(" 标注了返回的引用值,所以返回的引用值就能保证在 "),n("code",[t._v("x")]),t._v(" 和 "),n("code",[t._v("y")]),t._v(" 中较短的那个生命周期结束之前保持有效。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们看看如何通过传递拥有不同具体生命周期的引用来限制 "),s("code",[this._v("longest")]),this._v(" 函数的使用。示例 10-23 是一个很直观的例子。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" longest"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# x\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# y\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" string1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"long string is long"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" string2 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"xyz"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" result "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("longest")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("string1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("as_str")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" string2"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("as_str")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"The longest string is {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" result"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-23:通过拥有不同的具体生命周期的 "),s("code",[this._v("String")]),this._v(" 值调用 "),s("code",[this._v("longest")]),this._v(" 函数")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在这个例子中,"),n("code",[t._v("string1")]),t._v(" 直到外部作用域结束都是有效的,"),n("code",[t._v("string2")]),t._v(" 则在内部作用域中是有效的,而 "),n("code",[t._v("result")]),t._v(" 则引用了一些直到内部作用域结束都是有效的值。借用检查器认可这些代码;它能够编译和运行,并打印出 "),n("code",[t._v("The longest string is long string is long")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("接下来,让我们尝试一个 "),n("code",[t._v("result")]),t._v(" 的引用的生命周期肯定比两个参数的要短的例子。将 "),n("code",[t._v("result")]),t._v(" 变量的声明从内部作用域中移动出来,但是将 "),n("code",[t._v("result")]),t._v(" 和 "),n("code",[t._v("string2")]),t._v(" 变量的赋值语句一同留在内部作用域里。接下来,我们将使用 "),n("code",[t._v("result")]),t._v(" 的 "),n("code",[t._v("println!")]),t._v(" 移动到内部作用域之外,就在其结束之后。注意示例 10-24 中的代码不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let string1 = String::from("long string is long");\n let result;\n {\n let string2 = String::from("xyz");\n result = longest(string1.as_str(), string2.as_str());\n }\n println!("The longest string is {}", result);\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-24:在 "),s("code",[this._v("string2")]),this._v(" 离开作用域之后使用 "),s("code",[this._v("result")]),this._v(" 的尝试不能编译")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0597]: `string2` does not live long enough\n --\x3e src/main.rs:15:5\n |\n14 | result = longest(string1.as_str(), string2.as_str());\n | ------- borrow occurs here\n15 | }\n | ^ `string2` dropped here while still borrowed\n16 | println!("The longest string is {}", result);\n17 | }\n | - borrowed value needs to live until here\n')])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("错误表明为了保证 "),n("code",[t._v("println!")]),t._v(" 中的 "),n("code",[t._v("result")]),t._v(" 是有效的,"),n("code",[t._v("string2")]),t._v(" 需要直到外部作用域结束都是有效的。Rust 知道这些是因为("),n("code",[t._v("longest")]),t._v(")函数的参数和返回值都使用了相同的生命周期参数 "),n("code",[t._v("'a")]),t._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("以人类的理解 "),n("code",[t._v("string1")]),t._v(" 更长,因此 "),n("code",[t._v("result")]),t._v(" 会包含指向 "),n("code",[t._v("string1")]),t._v(" 的引用。因为 "),n("code",[t._v("string1")]),t._v(" 尚未离开作用域,对于 "),n("code",[t._v("println!")]),t._v(" 来说 "),n("code",[t._v("string1")]),t._v(" 的引用仍然是有效的。然而,我们通过生命周期参数告诉 Rust 的是 "),n("code",[t._v("longest")]),t._v(" 函数返回的引用的生命周期应该与传入参数的生命周期中较短那个保持一致。因此,借用检查器不允许示例 10-24 中的代码,因为它可能会存在无效的引用。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("请尝试更多采用不同的值和不同生命周期的引用作为 "),s("code",[this._v("longest")]),this._v(" 函数的参数和返回值的实验。并在开始编译前猜想你的实验能否通过借用检查器,接着编译一下看看你的理解是否正确!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"深入理解生命周期"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#深入理解生命周期","aria-hidden":"true"}},[this._v("#")]),this._v(" 深入理解生命周期")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("指定生命周期参数的正确方式依赖函数具体的功能。例如,如果将 "),s("code",[this._v("longest")]),this._v(" 函数的实现修改为总是返回第一个参数而不是最长的字符串 slice,就不需要为参数 "),s("code",[this._v("y")]),this._v(" 指定一个生命周期。如下代码将能够编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" longest"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在这个例子中,我们为参数 "),n("code",[t._v("x")]),t._v(" 和返回值指定了生命周期参数 "),n("code",[t._v("'a")]),t._v(",不过没有为参数 "),n("code",[t._v("y")]),t._v(" 指定,因为 "),n("code",[t._v("y")]),t._v(" 的生命周期与参数 "),n("code",[t._v("x")]),t._v(" 和返回值的生命周期没有任何关系。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当从函数返回一个引用,返回值的生命周期参数需要与一个参数的生命周期参数相匹配。如果返回的引用 "),s("strong",[this._v("没有")]),this._v(" 指向任何一个参数,那么唯一的可能就是它指向一个函数内部创建的值,它将会是一个悬垂引用,因为它将会在函数结束时离开作用域。尝试考虑这个并不能编译的 "),s("code",[this._v("longest")]),this._v(" 函数实现:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn longest<'a>(x: &str, y: &str) -> &'a str {\n let result = String::from(\"really long string\");\n result.as_str()\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("即便我们为返回值指定了生命周期参数 "),s("code",[this._v("'a")]),this._v(",这个实现却编译失败了,因为返回值的生命周期与参数完全没有关联。这里是会出现的错误信息:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0597]: `result` does not live long enough\n --\x3e src/main.rs:3:5\n |\n3 | result.as_str()\n | ^^^^^^ does not live long enough\n4 | }\n | - borrowed value only lives until here\n |\nnote: borrowed value must be valid for the lifetime 'a as defined on the\nfunction body at 1:1...\n --\x3e src/main.rs:1:1\n |\n1 | / fn longest<'a>(x: &str, y: &str) -> &'a str {\n2 | | let result = String::from(\"really long string\");\n3 | | result.as_str()\n4 | | }\n | |_^\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("出现的问题是 "),s("code",[this._v("result")]),this._v(" 在 "),s("code",[this._v("longest")]),this._v(" 函数的结尾将离开作用域并被清理,而我们尝试从函数返回一个 "),s("code",[this._v("result")]),this._v(" 的引用。无法指定生命周期参数来改变悬垂引用,而且 Rust 也不允许我们创建一个悬垂引用。在这种情况,最好的解决方案是返回一个有所有权的数据类型而不是一个引用,这样函数调用者就需要负责清理这个值了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"结构体定义中的生命周期注解"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#结构体定义中的生命周期注解","aria-hidden":"true"}},[this._v("#")]),this._v(" 结构体定义中的生命周期注解")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("目前为止,我们只定义过有所有权类型的结构体。也可以定义存放引用的结构体,不过需要为结构体定义中的每一个引用添加生命周期注解。示例 10-25 中有一个存放了一个字符串 slice 的结构体 "),s("code",[this._v("ImportantExcerpt")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ImportantExcerpt"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n part"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" novel "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Call me Ishmael. Some years ago..."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" first_sentence "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" novel"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("split")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token char string"}},[t._v("'.'")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("expect")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v("\"Could not find a '.'\"")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" i "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" ImportantExcerpt "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" part"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" first_sentence "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-25:一个存放引用的结构体,所以其定义需要生命周期注解")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个结构体有一个字段,"),s("code",[this._v("part")]),this._v(",它存放了一个字符串 slice,这是一个引用。类似于泛型参数类型,必须在结构体名称后面的尖括号中声明泛型生命周期参数,以便在结构体定义中使用生命周期参数。这个注解意味着 "),s("code",[this._v("ImportantExcerpt")]),this._v(" 的示例不能比其 "),s("code",[this._v("part")]),this._v(" 字段中的引用存在的更久。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里的 "),n("code",[t._v("main")]),t._v(" 函数创建了一个 "),n("code",[t._v("ImportantExcerpt")]),t._v(" 的实例,它存放了变量 "),n("code",[t._v("novel")]),t._v(" 所拥有的 "),n("code",[t._v("String")]),t._v(" 的第一个句子的引用。"),n("code",[t._v("novel")]),t._v(" 的数据在 "),n("code",[t._v("ImportantExcerpt")]),t._v(" 实例创建之前就存在。另外,直到 "),n("code",[t._v("ImportantExcerpt")]),t._v(" 离开作用域之后 "),n("code",[t._v("novel")]),t._v(" 都不会离开作用域,所以 "),n("code",[t._v("ImportantExcerpt")]),t._v(" 实例中的引用是有效的")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"生命周期省略(lifetime-elision)"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#生命周期省略(lifetime-elision)","aria-hidden":"true"}},[this._v("#")]),this._v(" 生命周期省略(Lifetime Elision)")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("first_word")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" bytes "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("as_bytes")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("item"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" bytes"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("enumerate")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" item "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{attrs:{class:"token char string"}},[t._v("b' '")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v("..")]),t._v("i"),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token punctuation"}},[t._v("..")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 10-27:示例 4-9 定义了一个没有使用生命周期注解的函数,即便其参数和返回值都是引用")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn first_word<'a>(s: &'a str) -> &'a str {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("被编码进 Rust 引用分析的模式被称为 "),s("strong",[this._v("生命周期省略规则")]),this._v("("),s("em",[this._v("lifetime elision rules")]),this._v(")。这并不是需要程序员遵守的规则;这些规则是一系列特定的场景,此时编译器会考虑,如果代码符合这些场景,就无需明确指定生命周期。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("函数或方法的参数的生命周期被称为 "),n("strong",[t._v("输入生命周期")]),t._v("("),n("em",[t._v("input lifetimes")]),t._v("),而返回值的生命周期被称为 "),n("strong",[t._v("输出生命周期")]),t._v("("),n("em",[t._v("output lifetimes")]),t._v(")。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这些规则适用于 "),s("code",[this._v("fn")]),this._v(" 定义,以及 "),s("code",[this._v("impl")]),this._v(" 块。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第一条规则是每一个是引用的参数都有它自己的生命周期参数。换句话说就是,有一个引用参数的函数有一个生命周期参数:"),s("code",[this._v("fn foo<'a>(x: &'a i32)")]),this._v(",有两个引用参数的函数有两个不同的生命周期参数,"),s("code",[this._v("fn foo<'a, 'b>(x: &'a i32, y: &'b i32)")]),this._v(",依此类推。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第二条规则是如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数:"),s("code",[this._v("fn foo<'a>(x: &'a i32) -> &'a i32")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第三条规则是如果方法有多个输入生命周期参数,不过其中之一因为方法的缘故为 "),s("code",[this._v("&self")]),this._v(" 或 "),s("code",[this._v("&mut self")]),this._v(",那么 "),s("code",[this._v("self")]),this._v(" 的生命周期被赋给所有输出生命周期参数。这使得方法更容易读写,因为只需更少的符号。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("假设我们自己就是编译器。并应用这些规则来计算示例 10-26 中 "),s("code",[this._v("first_word")]),this._v(" 函数签名中的引用的生命周期。开始时签名中的引用并没有关联任何生命周期:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn first_word(s: &str) -> &str {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接着编译器应用第一条规则,也就是每个引用参数都有其自己的生命周期。我们像往常一样称之为 "),s("code",[this._v("'a")]),this._v(",所以现在签名看起来像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn first_word<'a>(s: &'a str) -> &str {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn first_word<'a>(s: &'a str) -> &'a str {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们再看看另一个例子,这次我们从示例 10-21 中没有生命周期参数的 "),s("code",[this._v("longest")]),this._v(" 函数开始:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn longest(x: &str, y: &str) -> &str {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &str {\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("再来应用第二条规则,它并不适用因为存在多于一个输入生命周期。再来看第三条规则,它同样也不适用因为没有 "),s("code",[this._v("self")]),this._v(" 参数。然后我们就没有更多规则了,不过还没有计算出返回值的类型的生命周期。这就是为什么在编译示例 10-21 的代码时会出现错误的原因:编译器使用所有已知的生命周期省略规则,不过仍不能计算出签名中所有引用的生命周期。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"方法定义中的生命周期注解"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#方法定义中的生命周期注解","aria-hidden":"true"}},[this._v("#")]),this._v(" 方法定义中的生命周期注解")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("(实现方法时)结构体字段的生命周期必须总是在 "),s("code",[this._v("impl")]),this._v(" 关键字之后声明并在结构体名称之后被使用,因为这些生命周期是结构体类型的一部分。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("impl")]),this._v(" 块里的方法签名中,引用可能与结构体字段中的引用相关联,也可能是独立的。另外,生命周期省略规则也经常让我们无需在方法签名中使用生命周期注解。让我们看看一些使用示例 10-25 中定义的结构体 "),s("code",[this._v("ImportantExcerpt")]),this._v(" 的例子。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("首先,这里有一个方法 "),s("code",[this._v("level")]),this._v("。其唯一的参数是 "),s("code",[this._v("self")]),this._v(" 的引用,而且返回值只是一个 "),s("code",[this._v("i32")]),this._v(",并不引用任何值:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ImportantExcerpt"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# part"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" ImportantExcerpt"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("level")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token number"}},[t._v("3")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("impl")]),this._v(" 之后和类型名称之后的生命周期参数是必要的,不过因为第一条生命周期规则我们并不必须标注 "),s("code",[this._v("self")]),this._v(" 引用的生命周期。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" ImportantExcerpt"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# part"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" ImportantExcerpt"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("announce_and_return_part")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" announcement"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Attention please: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" announcement"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("part\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里有两个输入生命周期,所以 Rust 应用第一条生命周期省略规则并给予 "),n("code",[t._v("&self")]),t._v(" 和 "),n("code",[t._v("announcement")]),t._v(" 他们各自的生命周期。接着,因为其中一个参数是 "),n("code",[t._v("&self")]),t._v(",返回值类型被赋予了 "),n("code",[t._v("&self")]),t._v(" 的生命周期,这样所有的生命周期都被计算出来了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"静态生命周期"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#静态生命周期","aria-hidden":"true"}},[this._v("#")]),this._v(" 静态生命周期")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里有一种特殊的生命周期值得讨论:"),s("code",[this._v("'static")]),this._v(",其生命周期存活于整个程序期间。所有的字符串字面值都拥有 "),s("code",[this._v("'static")]),this._v(" 生命周期,我们也可以选择像下面这样标注出来:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),t._v(" str "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"I have a static lifetime."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个字符串的文本被直接储存在程序的二进制文件中而这个文件总是可用的。因此所有的字符串字面值都是 "),s("code",[this._v("'static")]),this._v(" 的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("你可能在错误信息的帮助文本中见过使用 "),s("code",[this._v("'static")]),this._v(" 生命周期的建议,不过将引用指定为 "),s("code",[this._v("'static")]),this._v(" 之前,思考一下这个引用是否真的在整个程序的生命周期里都有效。你可能会考虑希望它一直有效,如果可能的话。大部分情况,代码中的问题是尝试创建一个悬垂引用或者可用的生命周期不匹配,请解决这些问题而不是指定一个 "),s("code",[this._v("'static")]),this._v(" 的生命周期。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"结合泛型类型参数、trait-bounds-和生命周期"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#结合泛型类型参数、trait-bounds-和生命周期","aria-hidden":"true"}},[this._v("#")]),this._v(" 结合泛型类型参数、trait bounds 和生命周期")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fmt"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Display"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" longest_with_an_announcement"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a,")]),t._v(" T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ann"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str\n "),n("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Display\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Announcement! {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ann"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" y"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n y\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这个是示例 10-22 中那个返回两个字符串 slice 中较长者的 "),n("code",[t._v("longest")]),t._v(" 函数,不过带有一个额外的参数 "),n("code",[t._v("ann")]),t._v("。"),n("code",[t._v("ann")]),t._v(" 的类型是泛型 "),n("code",[t._v("T")]),t._v(",它可以被放入任何实现了 "),n("code",[t._v("where")]),t._v(" 从句中指定的 "),n("code",[t._v("Display")]),t._v(" trait 的类型。这个额外的参数会在函数比较字符串 slice 的长度之前被打印出来,这也就是为什么 "),n("code",[t._v("Display")]),t._v(" trait bound 是必须的。因为生命周期也是泛型,所以生命周期参数 "),n("code",[t._v("'a")]),t._v(" 和泛型类型参数 "),n("code",[t._v("T")]),t._v(" 都位于函数名后的同一尖括号列表中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])}],!1,null,null,null);e.options.__file="ch10-03-lifetime-syntax.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/55.f82d7ef0.js b/src/.vuepress/dist/assets/js/55.f82d7ef0.js
new file mode 100644
index 0000000..78b16cc
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/55.f82d7ef0.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[55],{215:function(t,e,s){"use strict";s.r(e);var i=s(0),r=Object(i.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch11-00-testing.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch11-00-testing.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),s("p",[t._v("Edsger W. Dijkstra 在其 1972 年的文章【谦卑的程序员】(“The Humble Programmer”)中说到 “软件测试是证明 bug 存在的有效方法,而证明其不存在时则显得令人绝望的不足。”(“Program testing can be a very effective way to show the presence of bugs, but it is hopelessly inadequate for showing their absence.”)这并不意味着我们不该尽可能地测试软件!")]),t._v(" "),s("p",[t._v("程序的正确性意味着代码如我们期望的那样运行。Rust 是一个相当注重正确性的编程语言,不过正确性是一个难以证明的复杂主题。Rust 的类型系统在此问题上下了很大的功夫,不过它不可能捕获所有种类的错误。为此,Rust 也在语言本身包含了编写软件测试的支持。")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),s("p",[t._v("测试是一项复杂的技能:虽然不能在一个章节的篇幅中介绍如何编写好的测试的每个细节,但我们还是会讨论 Rust 测试功能的机制。我们会讲到编写测试时会用到的注解和宏,运行测试的默认行为和选项,以及如何将测试组织成单元测试和集成测试。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"编写自动化测试"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#编写自动化测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 编写自动化测试")])},function(){var t=this.$createElement,e=this._self._c||t;return e("blockquote",[e("p",[this._v("Program testing can be a very effective way to show the presence of bugs, but it is hopelessly inadequate for showing their absence.")]),this._v(" "),e("p",[this._v('Edsger W. Dijkstra, "The Humble Programmer" (1972)')]),this._v(" "),e("p",[this._v("软件测试是证明 bug 存在的有效方法,而证明其不存在时则显得令人绝望的不足。")]),this._v(" "),e("p",[this._v("Edsger W. Dijkstra,【谦卑的程序员】(1972)")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("例如,我们可以编写一个叫做 "),e("code",[this._v("add_two")]),this._v(" 的将传递给它的值加二的函数。它的签名有一个整型参数并返回一个整型值。当实现和编译这个函数时,Rust 会进行所有目前我们已经见过的类型检查和借用检查,例如,这些检查会确保我们不会传递 "),e("code",[this._v("String")]),this._v(" 或无效的引用给这个函数。Rust 所 "),e("strong",[this._v("不能")]),this._v(" 检查的是这个函数是否会准确的完成我们期望的工作:返回参数加二后的值,而不是比如说参数加 10 或减 50 的值!这也就是测试出场的地方。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("我们可以编写测试断言,比如说,当传递 "),e("code",[this._v("3")]),this._v(" 给 "),e("code",[this._v("add_two")]),this._v(" 函数时,返回值是 "),e("code",[this._v("5")]),this._v("。无论何时对代码进行修改,都可以运行测试来确保任何现存的正确行为没有被改变。")])}],!1,null,null,null);r.options.__file="ch11-00-testing.md";e.default=r.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/56.c9db2b25.js b/src/.vuepress/dist/assets/js/56.c9db2b25.js
new file mode 100644
index 0000000..4b40ccc
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/56.c9db2b25.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[56],{214:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch11-01-writing-tests.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch11-01-writing-tests.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit 820ac357f6cf0e866e5a8e7a9c57dd3e17e9f8ca")])]),t._v(" "),a("p",[t._v("Rust 中的测试函数是用来验证非测试代码是否按照期望的方式运行的。测试函数体通常执行如下三种操作:")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),a("p",[t._v("第七章当使用 Cargo 新建一个库项目时,它会自动为我们生成一个测试模块和一个测试函数。这有助于我们开始编写测试,因为这样每次开始新项目时不必去查找测试函数的具体结构和语法了。当然你也可以额外增加任意多的测试函数以及测试模块!")]),t._v(" "),a("p",[t._v("我们会通过实验那些自动生成的测试模版而不是实际编写测试代码来探索测试如何工作的一些方面。接着,我们会写一些真正的测试,调用我们编写的代码并断言他们的行为的正确性。")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),a("p",[a("code",[t._v("0 measured")]),t._v(" 统计是针对性能测试的。性能测试(benchmark tests)在编写本书时,仍只能用于 Rust 开发版(nightly Rust)。请查看 "),a("a",{attrs:{href:"https://doc.rust-lang.org/unstable-book/library-features/test.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("性能测试的文档"),a("OutboundLink")],1),t._v(" 了解更多。")]),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._m(22),t._v(" "),t._m(23),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),a("p",[t._v("它确实通过了!再来增加另一个测试,这一回断言一个更小的矩形不能放下一个更大的矩形:")]),t._v(" "),t._m(47),t._v(" "),t._m(48),t._m(49),t._v(" "),t._m(50),t._m(51),t._v(" "),t._m(52),a("p",[t._v("现在运行测试会产生:")]),t._v(" "),t._m(53),t._m(54),t._v(" "),t._m(55),t._v(" "),t._m(56),t._v(" "),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._m(60),t._v(" "),a("p",[t._v("测试通过了!")]),t._v(" "),t._m(61),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),a("p",[t._v("再次运行测试:")]),t._v(" "),t._m(65),t._m(66),t._v(" "),t._m(67),t._v(" "),t._m(68),t._v(" "),t._m(69),t._v(" "),t._m(70),t._v(" "),t._m(71),t._v(" "),a("p",[t._v("例如,比如说有一个根据人名进行问候的函数,而我们希望测试将传递给函数的人名显示在输出中:")]),t._v(" "),t._m(72),t._v(" "),t._m(73),t._m(74),t._v(" "),t._m(75),t._v(" "),t._m(76),a("p",[t._v("运行测试会产生:")]),t._v(" "),t._m(77),t._m(78),t._v(" "),t._m(79),a("p",[t._v("现在如果再次运行测试,将会看到更有价值的信息:")]),t._v(" "),t._m(80),a("p",[t._v("可以在测试输出中看到所取得的确切的值,这会帮助我们理解真正发生了什么,而不是期望发生什么。")]),t._v(" "),t._m(81),t._v(" "),t._m(82),t._v(" "),t._m(83),t._v(" "),t._m(84),t._v(" "),t._m(85),t._v(" "),t._m(86),t._m(87),t._v(" "),t._m(88),t._v(" "),t._m(89),t._m(90),t._v(" "),t._m(91),a("p",[t._v("如果运行示例 11-8 的测试,它会失败:")]),t._v(" "),t._m(92),t._m(93),t._v(" "),t._m(94),t._v(" "),t._m(95),t._v(" "),t._m(96),t._m(97),t._v(" "),t._m(98),t._v(" "),t._m(99),t._v(" "),t._m(100),t._m(101),t._v(" "),t._m(102),t._m(103),t._v(" "),t._m(104),t._v(" "),t._m(105),t._v(" "),t._m(106),t._m(107),t._v(" "),t._m(108)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"如何编写测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#如何编写测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 如何编写测试")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ol",[s("li",[this._v("设置任何所需的数据或状态")]),this._v(" "),s("li",[this._v("运行需要测试的代码")]),this._v(" "),s("li",[this._v("断言其结果是我们所期望的")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们看看 Rust 提供的专门用来编写测试的功能:"),s("code",[this._v("test")]),this._v(" 属性、一些宏和 "),s("code",[this._v("should_panic")]),this._v(" 属性。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"测试函数剖析"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#测试函数剖析","aria-hidden":"true"}},[this._v("#")]),this._v(" 测试函数剖析")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("作为最简单例子,Rust 中的测试就是一个带有 "),a("code",[t._v("test")]),t._v(" 属性注解的函数。属性(attribute)是关于 Rust 代码片段的元数据;第五章中结构体中用到的 "),a("code",[t._v("derive")]),t._v(" 属性就是一个例子。为了将一个函数变成测试函数,需要在 "),a("code",[t._v("fn")]),t._v(" 行之前加上 "),a("code",[t._v("#[test]")]),t._v("。当使用 "),a("code",[t._v("cargo test")]),t._v(" 命令运行测试时,Rust 会构建一个测试执行程序用来调用标记了 "),a("code",[t._v("test")]),t._v(" 属性的函数,并报告每一个测试是通过还是失败。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们创建一个新的库项目 "),s("code",[this._v("adder")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo new adder --lib\n Created library `adder` project\n$ cd adder\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("adder 库中 "),s("code",[this._v("src/lib.rs")]),this._v(" 的内容应该看起来如示例 11-1 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("it_works")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("2")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-1:由 "),s("code",[this._v("cargo new")]),this._v(" 自动生成的测试模块和函数")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("现在让我们暂时忽略 "),a("code",[t._v("tests")]),t._v(" 模块和 "),a("code",[t._v("#[cfg(test)]")]),t._v(" 注解,并只关注函数来了解其如何工作。注意 "),a("code",[t._v("fn")]),t._v(" 行之前的 "),a("code",[t._v("#[test]")]),t._v(":这个属性表明这是一个测试函数,这样测试执行者就知道将其作为测试处理。因为也可以在 "),a("code",[t._v("tests")]),t._v(" 模块中拥有非测试的函数来帮助我们建立通用场景或进行常见操作,所以需要使用 "),a("code",[t._v("#[test]")]),t._v(" 属性标明哪些函数是测试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("函数体通过使用 "),s("code",[this._v("assert_eq!")]),this._v(" 宏来断言 2 加 2 等于 4。一个典型的测试的格式,就是像这个例子中的断言一样。接下来运行就可以看到测试通过。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("cargo test")]),this._v(" 命令会运行项目中所有的测试,如示例 11-2 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test\n Compiling adder v0.1.0 (file:///projects/adder)\n Finished dev [unoptimized + debuginfo] target(s) in 0.22 secs\n Running target/debug/deps/adder-ce99bcc2479f4607\n\nrunning 1 test\ntest tests::it_works ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n Doc-tests adder\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-2:运行自动生成测试的输出")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("Cargo 编译并运行了测试。在 "),a("code",[t._v("Compiling")]),t._v("、"),a("code",[t._v("Finished")]),t._v(" 和 "),a("code",[t._v("Running")]),t._v(" 这几行之后,可以看到 "),a("code",[t._v("running 1 test")]),t._v(" 这一行。下一行显示了生成的测试函数的名称,它是 "),a("code",[t._v("it_works")]),t._v(",以及测试的运行结果,"),a("code",[t._v("ok")]),t._v("。接着可以看到全体测试运行结果的摘要:"),a("code",[t._v("test result: ok.")]),t._v(" 意味着所有测试都通过了。"),a("code",[t._v("1 passed; 0 failed")]),t._v(" 表示通过或失败的测试数量。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为之前我们并没有将任何测试标记为忽略,所以摘要中会显示 "),s("code",[this._v("0 ignored")]),this._v("。我们也没有过滤需要运行的测试,所以摘要中会显示"),s("code",[this._v("0 filtered out")]),this._v("。在下一部分 “控制测试如何运行” 会讨论忽略和过滤测试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("测试输出的中以 "),s("code",[this._v("Doc-tests adder")]),this._v(" 开头的这一部分是所有文档测试的结果。我们现在并没有任何文档测试,不过 Rust 会编译任何在 API 文档中的代码示例。这个功能帮助我们使文档和代码保持同步!在第十四章的 “文档注释” 部分会讲到如何编写文档测试。现在我们将忽略 "),s("code",[this._v("Doc-tests")]),this._v(" 部分的输出。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们改变测试的名称并看看这如何改变测试的输出。给 "),s("code",[this._v("it_works")]),this._v(" 函数起个不同的名字,比如 "),s("code",[this._v("exploration")]),this._v(",像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("exploration")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("2")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("并再次运行 "),s("code",[this._v("cargo test")]),this._v("。现在输出中将出现 "),s("code",[this._v("exploration")]),this._v(" 而不是 "),s("code",[this._v("it_works")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 1 test\ntest tests::exploration ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们增加另一个测试,不过这一次是一个会失败的测试!当测试函数中出现 panic 时测试就失败了。每一个测试都在一个新线程中运行,当主线程发现测试线程异常了,就将对应测试标记为失败。第九章讲到了最简单的造成 panic 的方法:调用 "),s("code",[this._v("panic!")]),this._v(" 宏。写入新测试 "),s("code",[this._v("another")]),this._v(" 后, "),s("code",[this._v("src/lib.rs")]),this._v(" 现在看起来如示例 11-3 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,panics extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('# fn main() {}\n#[cfg(test)]\nmod tests {\n #[test]\n fn exploration() {\n assert_eq!(2 + 2, 4);\n }\n\n #[test]\n fn another() {\n panic!("Make this test fail");\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-3:增加第二个因调用了 "),s("code",[this._v("panic!")]),this._v(" 而失败的测试")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("再次 "),s("code",[this._v("cargo test")]),this._v(" 运行测试。输出应该看起来像示例 11-4,它表明 "),s("code",[this._v("exploration")]),this._v(" 测试通过了而 "),s("code",[this._v("another")]),this._v(" 失败了:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 2 tests\ntest tests::exploration ... ok\ntest tests::another ... FAILED\n\nfailures:\n\n---- tests::another stdout ----\n thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:10:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n\nfailures:\n tests::another\n\ntest result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out\n\nerror: test failed\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-4:一个测试通过和一个测试失败的测试结果")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("test tests::another")]),t._v(" 这一行是 "),a("code",[t._v("FAILED")]),t._v(" 而不是 "),a("code",[t._v("ok")]),t._v(" 了。在单独测试结果和摘要之间多了两个新的部分:第一个部分显示了测试失败的详细原因。在这个例子中,"),a("code",[t._v("another")]),t._v(" 因为在"),a("em",[t._v("src/lib.rs")]),t._v(" 的第 10 行 "),a("code",[t._v("panicked at 'Make this test fail'")]),t._v(" 而失败。下一部分列出了所有失败的测试,这在有很多测试和很多失败测试的详细输出时很有帮助。我们可以通过使用失败测试的名称来只运行这个测试,以便调试;下一部分 “控制测试如何运行” 会讲到更多运行测试的方法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("最后是摘要行:总体上讲,测试结果是 "),s("code",[this._v("FAILED")]),this._v("。有一个测试通过和一个测试失败。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在我们见过不同场景中测试结果是什么样子的了,再来看看除 "),s("code",[this._v("panic!")]),this._v(" 之外的一些在测试中有帮助的宏吧。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-assert-宏来检查结果"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-assert-宏来检查结果","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("assert!")]),this._v(" 宏来检查结果")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("assert!")]),t._v(" 宏由标准库提供,在希望确保测试中一些条件为 "),a("code",[t._v("true")]),t._v(" 时非常有用。需要向 "),a("code",[t._v("assert!")]),t._v(" 宏提供一个求值为布尔值的参数。如果值是 "),a("code",[t._v("true")]),t._v(","),a("code",[t._v("assert!")]),t._v(" 什么也不做,同时测试会通过。如果值为 "),a("code",[t._v("false")]),t._v(","),a("code",[t._v("assert!")]),t._v(" 调用 "),a("code",[t._v("panic!")]),t._v(" 宏,这会导致测试失败。"),a("code",[t._v("assert!")]),t._v(" 宏帮助我们检查代码是否以期望的方式运行。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("回忆一下第五章中,示例 5-15 中有一个 "),a("code",[t._v("Rectangle")]),t._v(" 结构体和一个 "),a("code",[t._v("can_hold")]),t._v(" 方法,在示例 11-5 中再次使用他们。将他们放进 "),a("em",[t._v("src/lib.rs")]),t._v(" 并使用 "),a("code",[t._v("assert!")]),t._v(" 宏编写一些测试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(Debug)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n length"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("can_hold")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("Rectangle"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" bool "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("length "),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("length "),a("span",{attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("self")]),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width "),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" other"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("width\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-5:第五章中 "),s("code",[this._v("Rectangle")]),this._v(" 结构体和其 "),s("code",[this._v("can_hold")]),this._v(" 方法")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("can_hold")]),t._v(" 方法返回一个布尔值,这意味着它完美符合 "),a("code",[t._v("assert!")]),t._v(" 宏的使用场景。在示例 11-6 中,让我们编写一个 "),a("code",[t._v("can_hold")]),t._v(" 方法的测试来作为练习,这里创建一个长为 8 宽为 7 的 "),a("code",[t._v("Rectangle")]),t._v(" 实例,并假设它可以放得下另一个长为 5 宽为 1 的 "),a("code",[t._v("Rectangle")]),t._v(" 实例:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("super")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("larger_can_hold_smaller")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" larger "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" length"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("8")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("7")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" smaller "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" length"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("assert!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("larger"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("can_hold")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("smaller"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-6:一个 "),s("code",[this._v("can_hold")]),this._v(" 的测试,检查一个较大的矩形确实能放得下一个较小的矩形")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("注意在 "),a("code",[t._v("tests")]),t._v(" 模块中新增加了一行:"),a("code",[t._v("use super::*;")]),t._v("。"),a("code",[t._v("tests")]),t._v(" 是一个普通的模块,它遵循第七章 “私有性规则” 部分介绍的可见性规则。因为这是一个内部模块,要测试外部模块中的代码,需要将其引入到内部模块的作用域中。这里选择使用 glob 全局导入,以便在 "),a("code",[t._v("tests")]),t._v(" 模块中使用所有在外部模块定义的内容。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("我们将测试命名为 "),a("code",[t._v("larger_can_hold_smaller")]),t._v(",并创建所需的两个 "),a("code",[t._v("Rectangle")]),t._v(" 实例。接着调用 "),a("code",[t._v("assert!")]),t._v(" 宏并传递 "),a("code",[t._v("larger.can_hold(&smaller)")]),t._v(" 调用的结果作为参数。这个表达式预期会返回 "),a("code",[t._v("true")]),t._v(",所以测试应该通过。让我们拭目以待!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 1 test\ntest tests::larger_can_hold_smaller ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("super")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("larger_can_hold_smaller")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("smaller_cannot_hold_larger")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" larger "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" length"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("8")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("7")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" smaller "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Rectangle "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" length"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("5")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" width"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token function"}},[t._v("assert!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("!")]),t._v("smaller"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("can_hold")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("larger"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("因为这里 "),a("code",[t._v("can_hold")]),t._v(" 函数的正确结果是 "),a("code",[t._v("false")]),t._v(" ,我们需要将这个结果取反后传递给 "),a("code",[t._v("assert!")]),t._v(" 宏。因此 "),a("code",[t._v("can_hold")]),t._v(" 返回 "),a("code",[t._v("false")]),t._v(" 时测试就会通过:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 2 tests\ntest tests::smaller_cannot_hold_larger ... ok\ntest tests::larger_can_hold_smaller ... ok\n\ntest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("两个通过的测试!现在让我们看看如果引入一个 bug 的话测试结果会发生什么。将 "),s("code",[this._v("can_hold")]),this._v(" 方法中比较长度时本应使用大于号的地方改成小于号:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,not_desired_behavior extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("# fn main() {}\n# #[derive(Debug)]\n# pub struct Rectangle {\n# length: u32,\n# width: u32,\n# }\n// --snip--\n\nimpl Rectangle {\n pub fn can_hold(&self, other: &Rectangle) -> bool {\n self.length < other.length && self.width > other.width\n }\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 2 tests\ntest tests::smaller_cannot_hold_larger ... ok\ntest tests::larger_can_hold_smaller ... FAILED\n\nfailures:\n\n---- tests::larger_can_hold_smaller stdout ----\n thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed:\n larger.can_hold(&smaller)', src/lib.rs:22:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n\nfailures:\n tests::larger_can_hold_smaller\n\ntest result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("我们的测试捕获了 bug!因为 "),a("code",[t._v("larger.length")]),t._v(" 是 8 而 "),a("code",[t._v("smaller.length")]),t._v(" 是 5,"),a("code",[t._v("can_hold")]),t._v(" 中的长度比较现在因为 8 不小于 5 而返回 "),a("code",[t._v("false")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-assert-eq-和-assert-ne-宏来测试相等"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-assert-eq-和-assert-ne-宏来测试相等","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("assert_eq!")]),this._v(" 和 "),s("code",[this._v("assert_ne!")]),this._v(" 宏来测试相等")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("测试功能的一个常用方法是将需要测试代码的值与期望值做比较,并检查是否相等。可以通过向 "),a("code",[t._v("assert!")]),t._v(" 宏传递一个使用 "),a("code",[t._v("==")]),t._v(" 运算符的表达式来做到。不过这个操作实在是太常见了,以至于标准库提供了一对宏来更方便的处理这些操作 —— "),a("code",[t._v("assert_eq!")]),t._v(" 和 "),a("code",[t._v("assert_ne!")]),t._v("。这两个宏分别比较两个值是相等还是不相等。当断言失败时他们也会打印出这两个值具体是什么,以便于观察测试 "),a("strong",[t._v("为什么")]),t._v(" 失败,而 "),a("code",[t._v("assert!")]),t._v(" 只会打印出它从 "),a("code",[t._v("==")]),t._v(" 表达式中得到了 "),a("code",[t._v("false")]),t._v(" 值,而不是导致 "),a("code",[t._v("false")]),t._v(" 的两个值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 11-7 中,让我们编写一个对其参数加二并返回结果的函数 "),s("code",[this._v("add_two")]),this._v("。接着使用 "),s("code",[this._v("assert_eq!")]),this._v(" 宏测试这个函数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("add_two")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("super")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("it_adds_two")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("4")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("add_two")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-7:使用 "),s("code",[this._v("assert_eq!")]),this._v(" 宏测试 "),s("code",[this._v("add_two")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 1 test\ntest tests::it_adds_two ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("传递给 "),a("code",[t._v("assert_eq!")]),t._v(" 宏的第一个参数 "),a("code",[t._v("4")]),t._v(" ,等于调用 "),a("code",[t._v("add_two(2)")]),t._v(" 的结果。测试中的这一行 "),a("code",[t._v("test tests::it_adds_two ... ok")]),t._v(" 中 "),a("code",[t._v("ok")]),t._v(" 表明测试通过!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在代码中引入一个 bug 来看看使用 "),s("code",[this._v("assert_eq!")]),this._v(" 的测试失败是什么样的。修改 "),s("code",[this._v("add_two")]),this._v(" 函数的实现使其加 3:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,not_desired_behavior extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("# fn main() {}\npub fn add_two(a: i32) -> i32 {\n a + 3\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 1 test\ntest tests::it_adds_two ... FAILED\n\nfailures:\n\n---- tests::it_adds_two stdout ----\n thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)`\n left: `4`,\n right: `5`', src/lib.rs:11:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n\nfailures:\n tests::it_adds_two\n\ntest result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("测试捕获到了 bug!"),a("code",[t._v("it_adds_two")]),t._v(" 测试失败,显示信息 "),a("code",[t._v("assertion failed: `(left == right)`")]),t._v(" 并表明 "),a("code",[t._v("left")]),t._v(" 是 "),a("code",[t._v("4")]),t._v(" 而 "),a("code",[t._v("right")]),t._v(" 是 "),a("code",[t._v("5")]),t._v("。这个信息有助于我们开始调试:它说 "),a("code",[t._v("assert_eq!")]),t._v(" 的 "),a("code",[t._v("left")]),t._v(" 参数是 "),a("code",[t._v("4")]),t._v(",而 "),a("code",[t._v("right")]),t._v(" 参数,也就是 "),a("code",[t._v("add_two(2)")]),t._v(" 的结果,是 "),a("code",[t._v("5")]),t._v("。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("需要注意的是,在一些语言和测试框架中,断言两个值相等的函数的参数叫做 "),a("code",[t._v("expected")]),t._v(" 和 "),a("code",[t._v("actual")]),t._v(",而且指定参数的顺序是很关键的。然而在 Rust 中,他们则叫做 "),a("code",[t._v("left")]),t._v(" 和 "),a("code",[t._v("right")]),t._v(",同时指定期望的值和被测试代码产生的值的顺序并不重要。这个测试中的断言也可以写成 "),a("code",[t._v("assert_eq!(add_two(2), 4)")]),t._v(",这时失败信息会变成 "),a("code",[t._v("assertion failed: `(left == right)`")]),t._v(" 其中 "),a("code",[t._v("left")]),t._v(" 是 "),a("code",[t._v("5")]),t._v(" 而 "),a("code",[t._v("right")]),t._v(" 是 "),a("code",[t._v("4")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("assert_ne!")]),this._v(" 宏在传递给它的两个值不相等时通过,而在相等时失败。在代码按预期运行,我们不确定值 "),s("strong",[this._v("会")]),this._v(" 是什么,不过能确定值绝对 "),s("strong",[this._v("不会")]),this._v(" 是什么的时候,这个宏最有用处。例如,如果一个函数保证会以某种方式改变其输出,不过这种改变方式是由运行测试时是星期几来决定的,这时最好的断言可能就是函数的输出不等于其输入。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("assert_eq!")]),t._v(" 和 "),a("code",[t._v("assert_ne!")]),t._v(" 宏在底层分别使用了 "),a("code",[t._v("==")]),t._v(" 和 "),a("code",[t._v("!=")]),t._v("。当断言失败时,这些宏会使用调试格式打印出其参数,这意味着被比较的值必需实现了 "),a("code",[t._v("PartialEq")]),t._v(" 和 "),a("code",[t._v("Debug")]),t._v(" trait。所有的基本类型和大部分标准库类型都实现了这些 trait。对于自定义的结构体和枚举,需要实现 "),a("code",[t._v("PartialEq")]),t._v(" 才能断言他们的值是否相等。需要实现 "),a("code",[t._v("Debug")]),t._v(" 才能在断言失败时打印他们的值。因为这两个 trait 都是派生 trait,如第五章示例 5-12 所提到的,通常可以直接在结构体或枚举上添加 "),a("code",[t._v("#[derive(PartialEq, Debug)]")]),t._v(" 注解。附录 C “可派生 trait” 中有更多关于这些和其他派生 trait 的详细信息。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"自定义失败信息"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#自定义失败信息","aria-hidden":"true"}},[this._v("#")]),this._v(" 自定义失败信息")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("你也可以向 "),a("code",[t._v("assert!")]),t._v("、"),a("code",[t._v("assert_eq!")]),t._v(" 和 "),a("code",[t._v("assert_ne!")]),t._v(" 宏传递一个可选的失败信息参数,可以在测试失败时将自定义失败信息一同打印出来。任何在 "),a("code",[t._v("assert!")]),t._v(" 的一个必需参数和 "),a("code",[t._v("assert_eq!")]),t._v(" 和 "),a("code",[t._v("assert_ne!")]),t._v(" 的两个必需参数之后指定的参数都会传递给 "),a("code",[t._v("format!")]),t._v(" 宏(在第八章的“使用 "),a("code",[t._v("+")]),t._v(" 运算符或 "),a("code",[t._v("format!")]),t._v(" 宏拼接字符串”部分讨论过),所以可以传递一个包含 "),a("code",[t._v("{}")]),t._v(" 占位符的格式字符串和需要放入占位符的值。自定义信息有助于记录断言的意义;当测试失败时就能更好的理解代码出了什么问题。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("greeting")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" String "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("format!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Hello {}!"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" name"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("super")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("greeting_contains_name")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" result "),a("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("greeting")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Carol"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("assert!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),a("span",{attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{attrs:{class:"token function"}},[t._v("contains")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Carol"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个程序的需求还没有被确定,因此问候文本开头的 "),s("code",[this._v("Hello")]),this._v(" 文本很可能会改变。然而我们并不想在需求改变时不得不更新测试,所以相比检查 "),s("code",[this._v("greeting")]),this._v(" 函数返回的确切值,我们将仅仅断言输出的文本中包含输入参数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们通过将 "),s("code",[this._v("greeting")]),this._v(" 改为不包含 "),s("code",[this._v("name")]),this._v(" 来在代码中引入一个 bug 来测试失败时是怎样的:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,not_desired_behavior extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('# fn main() {}\npub fn greeting(name: &str) -> String {\n String::from("Hello!")\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 1 test\ntest tests::greeting_contains_name ... FAILED\n\nfailures:\n\n---- tests::greeting_contains_name stdout ----\n thread 'tests::greeting_contains_name' panicked at 'assertion failed:\nresult.contains(\"Carol\")', src/lib.rs:12:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n\nfailures:\n tests::greeting_contains_name\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("结果仅仅告诉了我们断言失败了和失败的行号。一个更有用的失败信息应该打印出 "),s("code",[this._v("greeting")]),this._v(" 函数的值。让我们为测试函数增加一个自定义失败信息参数:带占位符的格式字符串,以及 "),s("code",[this._v("greeting")]),this._v(" 函数的值:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('#[test]\nfn greeting_contains_name() {\n let result = greeting("Carol");\n assert!(\n result.contains("Carol"),\n "Greeting did not contain name, value was `{}`", result\n );\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("---- tests::greeting_contains_name stdout ----\n thread 'tests::greeting_contains_name' panicked at 'Greeting did not\ncontain name, value was `Hello!`', src/lib.rs:12:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-should-panic-检查-panic"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-should-panic-检查-panic","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("should_panic")]),this._v(" 检查 panic")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("除了检查代码是否返回期望的正确的值之外,检查代码是否按照期望处理错误也是很重要的。例如,考虑第九章示例 9-10 创建的 "),a("code",[t._v("Guess")]),t._v(" 类型。其他使用 "),a("code",[t._v("Guess")]),t._v(" 的代码都是基于 "),a("code",[t._v("Guess")]),t._v(" 实例仅有的值范围在 1 到 100 的前提。可以编写一个测试来确保创建一个超出范围的值的 "),a("code",[t._v("Guess")]),t._v(" 实例会 panic。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以通过对函数增加另一个属性 "),s("code",[this._v("should_panic")]),this._v(" 来实现这些。这个属性在函数中的代码 panic 时会通过,而在其中的代码没有 panic 时失败。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 11-8 展示了一个检查 "),s("code",[this._v("Guess::new")]),this._v(" 是否按照我们的期望出错的测试:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Guess "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Guess "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Guess "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" value "),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("||")]),t._v(" value "),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("100")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("panic!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Guess value must be between 1 and 100, got {}."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" value"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n Guess "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("super")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[should_panic]")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("greater_than_100")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Guess"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("200")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-8:测试会造成 "),s("code",[this._v("panic!")]),this._v(" 的条件")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("#[should_panic]")]),this._v(" 属性位于 "),s("code",[this._v("#[test]")]),this._v(" 之后,对应的测试函数之前。让我们看看测试通过时它是什么样子:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 1 test\ntest tests::greater_than_100 ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("看起来不错!现在在代码中引入 bug,移除 "),s("code",[this._v("new")]),this._v(" 函数在值大于 100 时会 panic 的条件:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,not_desired_behavior extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('# fn main() {}\n# pub struct Guess {\n# value: i32,\n# }\n#\n// --snip--\n\nimpl Guess {\n pub fn new(value: i32) -> Guess {\n if value < 1 {\n panic!("Guess value must be between 1 and 100, got {}.", value);\n }\n\n Guess {\n value\n }\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 1 test\ntest tests::greater_than_100 ... FAILED\n\nfailures:\n\nfailures:\n tests::greater_than_100\n\ntest result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这回并没有得到非常有用的信息,不过一旦我们观察测试函数,会发现它标注了 "),s("code",[this._v("#[should_panic]")]),this._v("。这个错误意味着代码中函数 "),s("code",[this._v("Guess::new(200)")]),this._v(" 并没有产生 panic。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("然而 "),a("code",[t._v("should_panic")]),t._v(" 测试结果可能会非常含糊不清,因为它只是告诉我们代码并没有产生 panic。"),a("code",[t._v("should_panic")]),t._v(" 甚至在一些不是我们期望的原因而导致 panic 时也会通过。为了使 "),a("code",[t._v("should_panic")]),t._v(" 测试结果更精确,我们可以给 "),a("code",[t._v("should_panic")]),t._v(" 属性增加一个可选的 "),a("code",[t._v("expected")]),t._v(" 参数。测试工具会确保错误信息中包含其提供的文本。例如,考虑示例 11-9 中修改过的 "),a("code",[t._v("Guess")]),t._v(",这里 "),a("code",[t._v("new")]),t._v(" 函数根据其值是过大还或者过小而提供不同的 panic 信息:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[t._v("# "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("main")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Guess "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# value"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),a("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n"),a("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Guess "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Guess "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" value "),a("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("panic!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Guess value must be greater than or equal to 1, got {}."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n value"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" value "),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("100")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("panic!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"Guess value must be less than or equal to 100, got {}."')]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n value"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n Guess "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("super")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v('#[should_panic(expected = "Guess value must be less than or equal to 100")]')]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("greater_than_100")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Guess"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("new")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("200")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-9:一个会带有特定错误信息的 "),s("code",[this._v("panic!")]),this._v(" 条件的测试")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这个测试会通过,因为 "),a("code",[t._v("should_panic")]),t._v(" 属性中 "),a("code",[t._v("expected")]),t._v(" 参数提供的值是 "),a("code",[t._v("Guess::new")]),t._v(" 函数 panic 信息的子串。我们可以指定期望的整个 panic 信息,在这个例子中是 "),a("code",[t._v("Guess value must be less than or equal to 100, got 200.")]),t._v(" 。 "),a("code",[t._v("expected")]),t._v(" 信息的选择取决于 panic 信息有多独特或动态,和你希望测试有多准确。在这个例子中,错误信息的子字符串足以确保函数在 "),a("code",[t._v("else if value > 100")]),t._v(" 的情况下运行。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("为了观察带有 "),a("code",[t._v("expected")]),t._v(" 信息的 "),a("code",[t._v("should_panic")]),t._v(" 测试失败时会发生什么,让我们再次引入一个 bug,将 "),a("code",[t._v("if value < 1")]),t._v(" 和 "),a("code",[t._v("else if value > 100")]),t._v(" 的代码块对换:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,not_desired_behavior extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('if value < 1 {\n panic!("Guess value must be less than or equal to 100, got {}.", value);\n} else if value > 100 {\n panic!("Guess value must be greater than or equal to 1, got {}.", value);\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这一次运行 "),s("code",[this._v("should_panic")]),this._v(" 测试,它会失败:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 1 test\ntest tests::greater_than_100 ... FAILED\n\nfailures:\n\n---- tests::greater_than_100 stdout ----\n thread 'tests::greater_than_100' panicked at 'Guess value must be\ngreater than or equal to 1, got 200.', src/lib.rs:11:12\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\nnote: Panic did not include expected string 'Guess value must be less than or\nequal to 100'\n\nfailures:\n tests::greater_than_100\n\ntest result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("失败信息表明测试确实如期望 panic 了,不过 panic 信息中并没有包含 "),s("code",[this._v("expected")]),this._v(" 信息 "),s("code",[this._v("'Guess value must be less than or equal to 100'")]),this._v("。而我们得到的 panic 信息是 "),s("code",[this._v("'Guess value must be greater than or equal to 1, got 200.'")]),this._v("。这样就可以开始寻找 bug 在哪了!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"将-result-t-e-用于测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#将-result-t-e-用于测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 将 "),s("code",[this._v("Result")]),this._v(" 用于测试")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("目前为止,我们编写的测试在失败时就会 panic。也可以使用 "),s("code",[this._v("Result")]),this._v(" 编写测试!这里是第一个例子采用了 Result:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("it_works")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),a("span",{attrs:{class:"token operator"}},[t._v("<")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" String"),a("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("2")]),t._v(" "),a("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("4")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("Ok")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("Err")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("String"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token function"}},[t._v("from")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token string"}},[t._v('"two plus two does not equal four"')]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[t._v("这里我们将 "),a("code",[t._v("it_works")]),t._v(" 改为返回 Result。同时在函数体中,在成功时返回 "),a("code",[t._v("Ok(())")]),t._v(" 而不是 "),a("code",[t._v("assert_eq!")]),t._v(",而失败时返回带有 "),a("code",[t._v("String")]),t._v(" 的 "),a("code",[t._v("Err")]),t._v("。跟之前一样,这个测试可能成功或失败,不过不再通过 panic,可以通过 "),a("code",[t._v("Result")]),t._v(" 来判断结果。为此不能在对这些函数使用 "),a("code",[t._v("#[should_panic]")]),t._v(";而是应该返回 "),a("code",[t._v("Err")]),t._v("!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在你知道了几种编写测试的方法,让我们看看运行测试时会发生什么,和可以用于 "),s("code",[this._v("cargo test")]),this._v(" 的不同选项。")])}],!1,null,null,null);e.options.__file="ch11-01-writing-tests.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/57.bc11287b.js b/src/.vuepress/dist/assets/js/57.bc11287b.js
new file mode 100644
index 0000000..eb03ff0
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/57.bc11287b.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[57],{213:function(t,s,e){"use strict";e.r(s);var n=e(0),a=Object(n.a)({},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[t._m(0),t._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch11-02-running-tests.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch11-02-running-tests.md"),e("OutboundLink")],1),t._v(" "),e("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),e("p",[t._v("当运行多个测试时,他们默认使用线程来并行的运行。这意味着测试会更快的运行完毕,所以你可以更快的得到代码能否工作的反馈。因为测试是在同时运行的,你应该确保测试不能相互依赖,或依赖任何共享的状态,包括依赖共享的环境,比如当前工作目录或者环境变量。")]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),e("p",[t._v("例如,示例 11-10 有一个无意义的函数,它打印出其参数的值并接着返回 10。接着还有一个会通过的测试和一个会失败的测试:")]),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),e("p",[t._v("如果没有传递任何参数就运行测试,如你所见,所有测试都会并行运行:")]),t._v(" "),t._m(27),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._m(43),t._v(" "),t._m(44),t._m(45)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"控制测试如何运行"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#控制测试如何运行","aria-hidden":"true"}},[this._v("#")]),this._v(" 控制测试如何运行")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("就像 "),e("code",[t._v("cargo run")]),t._v(" 会编译代码并运行生成的二进制文件一样,"),e("code",[t._v("cargo test")]),t._v(" 在测试模式下编译代码并运行生成的测试二进制文件。可以指定命令行参数来改变 "),e("code",[t._v("cargo test")]),t._v(" 的默认行为。例如,"),e("code",[t._v("cargo test")]),t._v(" 生成的二进制文件的默认行为是并行的运行所有测试,并截获测试运行过程中产生的输出,阻止他们被显示出来,使得阅读测试结果相关的内容变得更容易。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("这些选项的一部分可以传递给 "),e("code",[t._v("cargo test")]),t._v(",而另一些则需要传递给生成的测试二进制文件。为了分隔两种类型的参数,首先列出传递给 "),e("code",[t._v("cargo test")]),t._v(" 的参数,接着是分隔符 "),e("code",[t._v("--")]),t._v(",再之后是传递给测试二进制文件的参数。运行 "),e("code",[t._v("cargo test --help")]),t._v(" 会告诉你 "),e("code",[t._v("cargo test")]),t._v(" 的相关参数,而运行 "),e("code",[t._v("cargo test -- --help")]),t._v(" 则会告诉你可以在分隔符 "),e("code",[t._v("--")]),t._v(" 之后使用的相关参数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"并行或连续的运行测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#并行或连续的运行测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 并行或连续的运行测试")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("举个例子,每一个测试都运行一些代码,假设这些代码都在硬盘上创建一个 "),s("em",[this._v("test-output.txt")]),this._v(" 文件并写入一些数据。接着每一个测试都读取文件中的数据并断言这个文件包含特定的值,而这个值在每个测试中都是不同的。因为所有测试都是同时运行的,一个测试可能会在另一个测试读写文件过程中修改了文件。那么第二个测试就会失败,并不是因为代码不正确,而是因为测试并行运行时相互干扰。一个解决方案是使每一个测试读写不同的文件;另一个解决方案是一次运行一个测试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果你不希望测试并行运行,或者想要更加精确的控制线程的数量,可以传递 "),s("code",[this._v("--test-threads")]),this._v(" 参数和希望使用线程的数量给测试二进制文件。例如:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test -- --test-threads=1\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里将测试线程设置为 "),s("code",[this._v("1")]),this._v(",告诉程序不要使用任何并行机制。这也会比并行运行花费更多时间,不过在有共享的状态时,测试就不会潜在的相互干扰了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"显示函数输出"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#显示函数输出","aria-hidden":"true"}},[this._v("#")]),this._v(" 显示函数输出")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("默认情况下,当测试通过时,Rust 的测试库会截获打印到标准输出的所有内容。比如在测试中调用了 "),s("code",[this._v("println!")]),this._v(" 而测试通过了,我们将不会在终端看到 "),s("code",[this._v("println!")]),this._v(" 的输出:只会看到说明测试通过的提示行。如果测试失败了,则会看到所有标准输出和其他错误信息。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,panics extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn prints_and_returns_10(a: i32) -> i32 {\n println!("I got the value {}", a);\n 10\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn this_test_will_pass() {\n let value = prints_and_returns_10(4);\n assert_eq!(10, value);\n }\n\n #[test]\n fn this_test_will_fail() {\n let value = prints_and_returns_10(8);\n assert_eq!(5, value);\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-10:一个调用了 "),s("code",[this._v("println!")]),this._v(" 的函数的测试")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("运行 "),s("code",[this._v("cargo test")]),this._v(" 将会看到这些测试的输出:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 2 tests\ntest tests::this_test_will_pass ... ok\ntest tests::this_test_will_fail ... FAILED\n\nfailures:\n\n---- tests::this_test_will_fail stdout ----\n I got the value 8\nthread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)`\n left: `5`,\n right: `10`', src/lib.rs:19:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n\nfailures:\n tests::this_test_will_fail\n\ntest result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("注意输出中不会出现测试通过时打印的内容,即 "),s("code",[this._v("I got the value 4")]),this._v("。因为当测试通过时,这些输出会被截获。失败测试的输出 "),s("code",[this._v("I got the value 8")]),this._v(" ,则出现在输出的测试摘要部分,同时也显示了测试失败的原因。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果你希望也能看到通过的测试中打印的值,截获输出的行为可以通过 "),s("code",[this._v("--nocapture")]),this._v(" 参数来禁用:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test -- --nocapture\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用 "),s("code",[this._v("--nocapture")]),this._v(" 参数再次运行示例 11-10 中的测试会显示如下输出:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 2 tests\nI got the value 4\nI got the value 8\ntest tests::this_test_will_pass ... ok\nthread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)`\n left: `5`,\n right: `10`', src/lib.rs:19:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\ntest tests::this_test_will_fail ... FAILED\n\nfailures:\n\nfailures:\n tests::this_test_will_fail\n\ntest result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("注意测试的输出和测试结果的输出是相互交叉的;这是由于测试是并行运行的,也就是上一部分讲到的。尝试一同使用 "),s("code",[this._v("--test-threads=1")]),this._v(" 和 "),s("code",[this._v("--nocapture")]),this._v(" 功能来看看输出是什么样子!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"通过名称来运行测试的子集"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#通过名称来运行测试的子集","aria-hidden":"true"}},[this._v("#")]),this._v(" 通过名称来运行测试的子集")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("有时运行整个测试集会耗费很长时间。如果你负责特定位置的代码,你可能会希望只运行这些代码相关的测试。你可以向 "),s("code",[this._v("cargo test")]),this._v(" 传递希望运行的测试的部分名称作为参数来选择运行哪些测试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为了展示如何运行测试的子集,示例 11-11 为 "),s("code",[this._v("add_two")]),this._v(" 函数创建了三个测试,我们可以选择具体运行哪一个:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("add_two")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a "),e("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("2")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),e("span",{attrs:{class:"token keyword"}},[t._v("super")]),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),e("span",{attrs:{class:"token operator"}},[t._v("*")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("add_two_and_two")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("4")]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("add_two")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("2")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("add_three_and_two")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("5")]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("add_two")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("3")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("one_hundred")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("102")]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("add_two")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("100")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-11:不同名称的三个测试")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 3 tests\ntest tests::add_two_and_two ... ok\ntest tests::add_three_and_two ... ok\ntest tests::one_hundred ... ok\n\ntest result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"运行单个测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#运行单个测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 运行单个测试")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以向 "),s("code",[this._v("cargo test")]),this._v(" 传递任意测试的名称来只运行这个测试:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test one_hundred\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running target/debug/deps/adder-06a75b4a1f2515e9\n\nrunning 1 test\ntest tests::one_hundred ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("只有名称为 "),s("code",[this._v("one_hundred")]),this._v(" 的测试被运行了;因为其余两个测试并不匹配这个名称。测试输出在摘要行的结尾显示了 "),s("code",[this._v("2 filtered out")]),this._v(" 表明还存在比本次所运行的测试更多的测试被过滤掉了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不能像这样指定多个测试名称;只有传递给 "),s("code",[this._v("cargo test")]),this._v(" 的第一个值才会被使用。不过有运行多个测试的方法。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"过滤运行多个测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#过滤运行多个测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 过滤运行多个测试")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们可以指定部分测试的名称,任何名称匹配这个名称的测试会被运行。例如,因为头两个测试的名称包含 "),s("code",[this._v("add")]),this._v(",可以通过 "),s("code",[this._v("cargo test add")]),this._v(" 来运行这两个测试:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test add\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running target/debug/deps/adder-06a75b4a1f2515e9\n\nrunning 2 tests\ntest tests::add_two_and_two ... ok\ntest tests::add_three_and_two ... ok\n\ntest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这运行了所有名字中带有 "),s("code",[this._v("add")]),this._v(" 的测试,也过滤掉了名为 "),s("code",[this._v("one_hundred")]),this._v(" 的测试。同时注意测试所在的模块也是测试名称的一部分,所以可以通过模块名来运行一个模块中的所有测试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"忽略某些测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#忽略某些测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 忽略某些测试")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("有时一些特定的测试执行起来是非常耗费时间的,所以在大多数运行 "),s("code",[this._v("cargo test")]),this._v(" 的时候希望能排除他们。虽然可以通过参数列举出所有希望运行的测试来做到,也可以使用 "),s("code",[this._v("ignore")]),this._v(" 属性来标记耗时的测试并排除他们,如下所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n"),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("it_works")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("2")]),t._v(" "),e("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("2")]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("4")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n"),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[ignore]")]),t._v("\n"),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("expensive_test")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token comment"}},[t._v("// code that takes an hour to run")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("对于想要排除的测试,我们在 "),e("code",[t._v("#[test]")]),t._v(" 之后增加了 "),e("code",[t._v("#[ignore]")]),t._v(" 行。现在如果运行测试,就会发现 "),e("code",[t._v("it_works")]),t._v(" 运行了,而 "),e("code",[t._v("expensive_test")]),t._v(" 没有运行:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test\n Compiling adder v0.1.0 (file:///projects/adder)\n Finished dev [unoptimized + debuginfo] target(s) in 0.24 secs\n Running target/debug/deps/adder-ce99bcc2479f4607\n\nrunning 2 tests\ntest expensive_test ... ignored\ntest it_works ... ok\n\ntest result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("expensive_test")]),this._v(" 被列为 "),s("code",[this._v("ignored")]),this._v(",如果我们只希望运行被忽略的测试,可以使用 "),s("code",[this._v("cargo test -- --ignored")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test -- --ignored\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running target/debug/deps/adder-ce99bcc2479f4607\n\nrunning 1 test\ntest expensive_test ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("通过控制运行哪些测试,你可以确保能够快速地运行 "),s("code",[this._v("cargo test")]),this._v(" 。当某个时刻需要检查 "),s("code",[this._v("ignored")]),this._v(" 测试的结果,而且你也有时间等待这个结果的话,就可以选择执行 "),s("code",[this._v("cargo test -- --ignored")]),this._v("。")])}],!1,null,null,null);a.options.__file="ch11-02-running-tests.md";s.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/58.a1a44552.js b/src/.vuepress/dist/assets/js/58.a1a44552.js
new file mode 100644
index 0000000..cd64f70
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/58.a1a44552.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[58],{212:function(t,s,e){"use strict";e.r(s);var n=e(0),a=Object(n.a)({},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[t._m(0),t._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch11-03-test-organization.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch11-03-test-organization.md"),e("OutboundLink")],1),t._v(" "),e("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),e("p",[t._v("为了保证你的库能够按照你的预期运行,从独立和整体的角度编写这两类测试都是非常重要的。")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._v(" "),t._m(50),t._v(" "),e("p",[t._v("Rust 的测试功能提供了一个确保即使你改变了函数的实现方式,也能继续以期望的方式运行的途径。单元测试独立地验证库的不同部分,也能够测试私有函数实现细节。集成测试则检查多个部分是否能结合起来正确地工作,并像其他外部代码那样测试库的公有 API。即使 Rust 的类型系统和所有权规则可以帮助避免一些 bug,不过测试对于减少代码中不符合期望行为的逻辑 bug 仍然是很重要的。")]),t._v(" "),e("p",[t._v("让我们将本章和其他之前章节所学的知识组合起来,在下一章一起编写一个项目!")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"测试的组织结构"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#测试的组织结构","aria-hidden":"true"}},[this._v("#")]),this._v(" 测试的组织结构")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("本章一开始就提到,测试是一个复杂的概念,而且不同的开发者也采用不同的技术和组织。Rust 社区倾向于根据测试的两个主要分类来考虑问题:"),e("strong",[t._v("单元测试")]),t._v("("),e("em",[t._v("unit tests")]),t._v(")与 "),e("strong",[t._v("集成测试")]),t._v("("),e("em",[t._v("integration tests")]),t._v(")。单元测试倾向于更小而更集中,在隔离的环境中一次测试一个模块,或者是测试私有接口。而集成测试对于你的库来说则完全是外部的。它们与其他外部代码一样,通过相同的方式使用你的代码,只测试公有接口而且每个测试都有可能会测试多个模块。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"单元测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#单元测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 单元测试")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("单元测试的目的是在与其他部分隔离的环境中测试每一个单元的代码,以便于快速而准确的某个单元的代码功能是否符合预期。单元测试与他们要测试的代码共同存放在位于 "),s("em",[this._v("src")]),this._v(" 目录下相同的文件中。规范是在每个文件中创建包含测试函数的 "),s("code",[this._v("tests")]),this._v(" 模块,并使用 "),s("code",[this._v("cfg(test)")]),this._v(" 标注模块。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"测试模块和-cfg-test"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#测试模块和-cfg-test","aria-hidden":"true"}},[this._v("#")]),this._v(" 测试模块和 "),s("code",[this._v("cfg(test)")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("测试模块的 "),e("code",[t._v("#[cfg(test)]")]),t._v(" 注解告诉 Rust 只在执行 "),e("code",[t._v("cargo test")]),t._v(" 时才编译和运行测试代码,而在运行 "),e("code",[t._v("cargo build")]),t._v(" 时不这么做。这在只希望构建库的时候可以节省编译时间,并且因为它们并没有包含测试,所以能减少编译产生的文件的大小。与之对应的集成测试因为位于另一个文件夹,所以它们并不需要 "),e("code",[t._v("#[cfg(test)]")]),t._v(" 注解。然而单元测试位于与源码相同的文件中,所以你需要使用 "),e("code",[t._v("#[cfg(test)]")]),t._v(" 来指定他们不应该被包含进编译结果中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("回忆本章第一部分新建的 "),s("code",[this._v("adder")]),this._v(" 项目吗,Cargo 为我们生成了如下代码:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("it_works")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("2")]),t._v(" "),e("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("2")]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("4")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("上述代码就是自动生成的测试模块。"),e("code",[t._v("cfg")]),t._v(" 属性代表 "),e("em",[t._v("configuration")]),t._v(" ,它告诉 Rust 其之后的项只应该被包含进特定配置选项中。在这个例子中,配置选项是 "),e("code",[t._v("test")]),t._v(",即 Rust 所提供的用于编译和运行测试的配置选项。通过使用 "),e("code",[t._v("cfg")]),t._v(" 属性,Cargo 只会在我们主动使用 "),e("code",[t._v("cargo test")]),t._v(" 运行测试时才编译测试代码。需要编译的不仅仅有标注为 "),e("code",[t._v("#[test]")]),t._v(" 的函数之外,还包括测试模块中可能存在的帮助函数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"测试私有函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#测试私有函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 测试私有函数")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("测试社区中一直存在关于是否应该对私有函数直接进行测试的论战,而在其他语言中想要测试私有函数是一件困难的,甚至是不可能的事。不过无论你坚持哪种测试意识形态,Rust 的私有性规则确实允许你测试私有函数。考虑示例 11-12 中带有私有函数 "),s("code",[this._v("internal_adder")]),this._v(" 的代码:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[t._v("# "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("main")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("add_two")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("internal_adder")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("2")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("internal_adder")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a "),e("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" b\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),e("span",{attrs:{class:"token keyword"}},[t._v("super")]),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),e("span",{attrs:{class:"token operator"}},[t._v("*")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("internal")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("4")]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("internal_adder")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token number"}},[t._v("2")]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{attrs:{class:"token number"}},[t._v("2")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-12:测试私有函数")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("注意 "),e("code",[t._v("internal_adder")]),t._v(" 函数并没有标记为 "),e("code",[t._v("pub")]),t._v(",不过因为测试也不过是 Rust 代码同时 "),e("code",[t._v("tests")]),t._v(" 也仅仅是另一个模块,我们完全可以在测试中导入和调用 "),e("code",[t._v("internal_adder")]),t._v("。如果你并不认为应该测试私有函数,Rust 也不会强迫你这么做。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"集成测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#集成测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 集成测试")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在 Rust 中,集成测试对于你需要测试的库来说完全是外部的。同其他使用库的代码一样使用库文件,也就是说它们只能调用一部分库中的公有 API 。集成测试的目的是测试库的多个部分能否一起正常工作。一些单独能正确运行的代码单元集成在一起也可能会出现问题,所以集成测试的覆盖率也是很重要的。为了创建集成测试,你需要先创建一个 "),s("em",[this._v("tests")]),this._v(" 目录。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"tests-目录"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#tests-目录","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("em",[this._v("tests")]),this._v(" 目录")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为了编写集成测试,需要在项目根目录创建一个 "),s("em",[this._v("tests")]),this._v(" 目录,与 "),s("em",[this._v("src")]),this._v(" 同级。Cargo 知道如何去寻找这个目录中的集成测试文件。接着可以随意在这个目录中创建任意多的测试文件,Cargo 会将每一个文件当作单独的 crate 来编译。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们来创建一个集成测试。保留示例 11-12 中 "),s("em",[this._v("src/lib.rs")]),this._v(" 的代码。创建一个 "),s("em",[this._v("tests")]),this._v(" 目录,新建一个文件 "),s("em",[this._v("tests/integration_test.rs")]),this._v(",并输入示例 11-13 中的代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: tests/integration_test.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("use adder;\n\n#[test]\nfn it_adds_two() {\n assert_eq!(4, adder::add_two(2));\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 11-13:一个 "),s("code",[this._v("adder")]),this._v(" crate 中函数的集成测试")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们在顶部增加了 "),s("code",[this._v("extern crate adder")]),this._v(",这在单元测试中是不需要的。这是因为每一个 "),s("code",[this._v("tests")]),this._v(" 目录中的测试文件都是完全独立的 crate,所以需要在每一个文件中导入库。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("并不需要将 "),e("em",[t._v("tests/integration_test.rs")]),t._v(" 中的任何代码标注为 "),e("code",[t._v("#[cfg(test)]")]),t._v("。Cargo 对 "),e("code",[t._v("tests")]),t._v(" 文件夹特殊处理并只会在运行 "),e("code",[t._v("cargo test")]),t._v(" 时编译这个目录中的文件。现在就运行 "),e("code",[t._v("cargo test")]),t._v(" 试试:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test\n Compiling adder v0.1.0 (file:///projects/adder)\n Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs\n Running target/debug/deps/adder-abcabcabc\n\nrunning 1 test\ntest tests::internal ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n Running target/debug/deps/integration_test-ce99bcc2479f4607\n\nrunning 1 test\ntest it_adds_two ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n Doc-tests adder\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在有了三个部分的输出:单元测试、集成测试和文档测试。第一部分单元测试与我们之前见过的一样:每个单元测试一行(示例 11-12 中有一个叫做 "),s("code",[this._v("internal")]),this._v(" 的测试),接着是一个单元测试的摘要行。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("集成测试部分以行 "),s("code",[this._v("Running target/debug/deps/integration-test-ce99bcc2479f4607")]),this._v("(在输出最后的哈希值可能不同)开头。接下来每一行是一个集成测试中的测试函数,以及一个位于 "),s("code",[this._v("Doc-tests adder")]),this._v(" 部分之前的集成测试的摘要行。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们已经知道,单元测试函数越多,单元测试部分的结果行就会越多。同样的,在集成文件中增加的测试函数越多,也会在对应的测试结果部分增加越多的结果行。每一个集成测试文件有对应的测试结果部分,所以如果在 "),s("em",[this._v("tests")]),this._v(" 目录中增加更多文件,测试结果中就会有更多集成测试结果部分。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们仍然可以通过指定测试函数的名称作为 "),s("code",[this._v("cargo test")]),this._v(" 的参数来运行特定集成测试。也可以使用 "),s("code",[this._v("cargo test")]),this._v(" 的 "),s("code",[this._v("--test")]),this._v(" 后跟文件的名称来运行某个特定集成测试文件中的所有测试:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test --test integration_test\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running target/debug/integration_test-952a27e0126bb565\n\nrunning 1 test\ntest it_adds_two ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个命令只运行了 "),s("em",[this._v("tests")]),this._v(" 目录中我们指定的文件 "),s("code",[this._v("integration_test.rs")]),this._v(" 中的测试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"集成测试中的子模块"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#集成测试中的子模块","aria-hidden":"true"}},[this._v("#")]),this._v(" 集成测试中的子模块")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("随着集成测试的增加,你可能希望在 "),s("code",[this._v("tests")]),this._v(" 目录增加更多文件以便更好的组织他们,例如根据测试的功能来将测试分组。正如我们之前提到的,每一个 "),s("em",[this._v("tests")]),this._v(" 目录中的文件都被编译为单独的 crate。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("将每个集成测试文件当作其自己的 crate 来对待,这更有助于创建单独的作用域,这种单独的作用域能提供更类似与最终使用者使用 crate 的环境。然而,正如你在第七章中学习的如何将代码分为模块和文件的知识,"),s("em",[this._v("tests")]),this._v(" 目录中的文件不能像 "),s("em",[this._v("src")]),this._v(" 中的文件那样共享相同的行为。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("当你有一些在多个集成测试文件都会用到的帮助函数,而你尝试按照第七章 “将模块移动到其他文件” 部分的步骤将他们提取到一个通用的模块中时, "),s("em",[this._v("tests")]),this._v(" 目录中不同文件的行为就会显得很明显。例如,如果我们创建了 "),s("em",[this._v("tests/common.rs")]),this._v(" 文件并将一个名叫 "),s("code",[this._v("setup")]),this._v(" 的函数放入其中,这里将放入一些我们希望能够在多个测试文件的多个测试函数中调用的代码:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: tests/common.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("setup")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token comment"}},[t._v("// 编写特定库测试所需的代码")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果再次运行测试,将会在测试结果中看到一个新的对应 "),s("em",[this._v("common.rs")]),this._v(" 文件的测试结果部分,即便这个文件并没有包含任何测试函数,也没有任何地方调用了 "),s("code",[this._v("setup")]),this._v(" 函数:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 1 test\ntest tests::internal ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n Running target/debug/deps/common-b8b07b6f1be2db70\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n Running target/debug/deps/integration_test-d993c68b431d39df\n\nrunning 1 test\ntest it_adds_two ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n Doc-tests adder\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们并不希望 "),s("code",[this._v("common")]),this._v(" 出现在测试结果中并显示 "),s("code",[this._v("running 0 tests")]),this._v(" 。我们只是希望能够在其他集成测试文件中分享一些代码罢了。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("为了避免 "),e("code",[t._v("common")]),t._v(" 出现在测试输出中,我们将创建 "),e("em",[t._v("tests/common/mod.rs")]),t._v(" ,而不是创建 "),e("em",[t._v("tests/common.rs")]),t._v(" 。在第七章的 “模块文件系统规则” 部分,对于拥有子模块的模块文件使用了 "),e("em",[t._v("module_name/mod.rs")]),t._v(" 命名规范,虽然这里 "),e("code",[t._v("common")]),t._v(" 并没有子模块,但是这样命名告诉 Rust 不要将 "),e("code",[t._v("common")]),t._v(" 看作一个集成测试文件。当将 "),e("code",[t._v("setup")]),t._v(" 函数代码移动到 "),e("em",[t._v("tests/common/mod.rs")]),t._v(" 并删除 "),e("em",[t._v("tests/common.rs")]),t._v(" 文件之后,测试输出中将不会出现这一部分。"),e("em",[t._v("tests")]),t._v(" 目录中的子目录不会被作为单独的 crate 编译或作为一个测试结果部分出现在测试输出中。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("一旦拥有了 "),e("em",[t._v("tests/common/mod.rs")]),t._v(",就可以将其作为模块以便在任何集成测试文件中使用。这里是一个 "),e("em",[t._v("tests/integration_test.rs")]),t._v(" 中调用 "),e("code",[t._v("setup")]),t._v(" 函数的 "),e("code",[t._v("it_adds_two")]),t._v(" 测试的例子:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: tests/integration_test.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("use adder;\n\nmod common;\n\n#[test]\nfn it_adds_two() {\n common::setup();\n assert_eq!(4, adder::add_two(2));\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("注意 "),s("code",[this._v("mod common;")]),this._v(" 声明与示例 7-25 中展示的模块声明相同。接着在测试函数中就可以调用 "),s("code",[this._v("common::setup()")]),this._v(" 了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"二进制-crate-的集成测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#二进制-crate-的集成测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 二进制 crate 的集成测试")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("如果项目是二进制 crate 并且只包含 "),e("em",[t._v("src/main.rs")]),t._v(" 而没有 "),e("em",[t._v("src/lib.rs")]),t._v(",这样就不可能在 "),e("em",[t._v("tests")]),t._v(" 目录创建集成测试并使用 "),e("code",[t._v("extern crate")]),t._v(" 导入 "),e("em",[t._v("src/main.rs")]),t._v(" 中定义的函数。只有库 crate 才会向其他 crate 暴露了可供调用和使用的函数;二进制 crate 只意在单独运行。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("为什么 Rust 二进制项目的结构明确采用 "),e("em",[t._v("src/main.rs")]),t._v(" 调用 "),e("em",[t._v("src/lib.rs")]),t._v(" 中的逻辑的方式?因为通过这种结构,集成测试 "),e("strong",[t._v("就可以")]),t._v(" 通过 "),e("code",[t._v("extern crate")]),t._v(" 测试库 crate 中的主要功能了,而如果这些重要的功能没有问题的话,"),e("em",[t._v("src/main.rs")]),t._v(" 中的少量代码也就会正常工作且不需要测试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"总结"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])}],!1,null,null,null);a.options.__file="ch11-03-test-organization.md";s.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/59.4afc94b7.js b/src/.vuepress/dist/assets/js/59.4afc94b7.js
new file mode 100644
index 0000000..631e971
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/59.4afc94b7.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[59],{211:function(t,e,r){"use strict";r.r(e);var _=r(0),v=Object(_.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),r("blockquote",[r("p",[r("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch12-00-an-io-project.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch12-00-an-io-project.md"),r("OutboundLink")],1),t._v(" "),r("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),r("p",[t._v("本章既是一个目前所学的很多技能的概括,也是一个更多标准库功能的探索。我们将构建一个与文件和命令行输入/输出交互的命令行工具来练习现在一些你已经掌握的 Rust 技能。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),r("p",[t._v("另外还会简要的讲到闭包、迭代器和 trait 对象,他们分别会在第十三章和第十七章中详细介绍。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"一个-i-o-项目:构建一个命令行程序"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#一个-i-o-项目:构建一个命令行程序","aria-hidden":"true"}},[this._v("#")]),this._v(" 一个 I/O 项目:构建一个命令行程序")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("p",[t._v("Rust 的运行速度、安全性、单二进制文件输出和跨平台支持使其成为创建命令行程序的绝佳选择,所以我们的项目将创建一个我们自己版本的经典命令行工具:"),r("code",[t._v("grep")]),t._v("。grep 是 “"),r("strong",[t._v("G")]),t._v("lobally search a "),r("strong",[t._v("R")]),t._v("egular "),r("strong",[t._v("E")]),t._v("xpression and "),r("strong",[t._v("P")]),t._v("rint.” 的首字母缩写。"),r("code",[t._v("grep")]),t._v(" 最简单的使用场景是在特定文件中搜索指定字符串。为此,"),r("code",[t._v("grep")]),t._v(" 获取一个文件名和一个字符串作为参数,接着读取文件并找到其中包含字符串参数的行,然后打印出这些行。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("在这个过程中,我们会展示如何让我们的命令行工具利用很多命令行工具中用到的终端功能。读取环境变量来使得用户可以配置工具的行为。打印到标准错误控制流("),e("code",[this._v("stderr")]),this._v(") 而不是标准输出("),e("code",[this._v("stdout")]),this._v("),例如这样用户可以选择将成功输出重定向到文件中的同时仍然在屏幕上显示错误信息。")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("p",[t._v("一位 Rust 社区的成员,Andrew Gallant,已经创建了一个功能完整且非常快速的 "),r("code",[t._v("grep")]),t._v(" 版本,叫做 "),r("code",[t._v("ripgrep")]),t._v("。相比之下,我们的 "),r("code",[t._v("grep")]),t._v(" 版本将非常简单,本章将教会你一些帮助理解像 "),r("code",[t._v("ripgrep")]),t._v(" 这样真实项目的背景知识。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("我们的 "),e("code",[this._v("grep")]),this._v(" 项目将会结合之前所学的一些内容:")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ul",[r("li",[t._v("代码组织(使用第七章学习的模块)")]),t._v(" "),r("li",[t._v("vector 和字符串(第八章,集合)")]),t._v(" "),r("li",[t._v("错误处理(第九章)")]),t._v(" "),r("li",[t._v("合理的使用 trait 和生命周期(第十章)")]),t._v(" "),r("li",[t._v("测试(第十一章)")])])}],!1,null,null,null);v.options.__file="ch12-00-an-io-project.md";e.default=v.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/6.4774d695.js b/src/.vuepress/dist/assets/js/6.4774d695.js
new file mode 100644
index 0000000..18bf679
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/6.4774d695.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{257:function(t,e,i){"use strict";i.r(e);var _=i(0),s=Object(_.a)({},function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"content"},[t._m(0),t._v(" "),i("blockquote",[i("p",[i("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/appendix-01-keywords.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("appendix-01-keywords.md"),i("OutboundLink")],1),t._v(" "),i("br"),t._v("\ncommit 32215c1d96c9046c0b553a05fa5ec3ede2e125c3")])]),t._v(" "),i("p",[t._v("下面的列表中是Rust正在使用或者以后会用关键字。因此,这些关键字不能被用作标识符,例如\n函数、变量、参数、结构体、模块、crate、常量、宏、静态值、属性、类型、trait 或生命周期\n的名字。")]),t._v(" "),t._m(1),t._v(" "),i("ul",[t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),i("li",[i("code",[t._v("where")]),t._v(" - 表示一个类型约束 "),i("router-link",{attrs:{to:"./ch13-01-closures.html#使用带有泛型和-fn-trait-的闭包"}},[t._v("[For example]")])],1),t._v(" "),t._m(35)]),t._v(" "),t._m(36),t._v(" "),i("p",[t._v("这些关键字没有目前任何功能,但是它们是 Rust 未来会使用的保留字。")]),t._v(" "),t._m(37)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"附录a-关键字"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#附录a-关键字","aria-hidden":"true"}},[this._v("#")]),this._v(" 附录A - 关键字")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"目前正在使用的关键字"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#目前正在使用的关键字","aria-hidden":"true"}},[this._v("#")]),this._v(" 目前正在使用的关键字")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("as")]),this._v(" - 强制类型转换或者对使用"),e("code",[this._v("use")]),this._v("和"),e("code",[this._v("extern crate")]),this._v("声明引入的项目重命名")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("break")]),this._v(" - 立刻退出循环")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("const")]),this._v(" - 定义常量或者 "),e("strong",[this._v("不变原生指针")]),this._v(" ("),e("em",[this._v("constant raw pointers")]),this._v(")")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("continue")]),this._v(" - 跳出本次循环,进入下一次循环")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("crate")]),this._v(" - 引入一个外部 "),e("strong",[this._v("crate")]),this._v(" 或一个代表 "),e("strong",[this._v("crate")]),this._v(" 的宏变量")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("else")]),this._v(" - 创建 "),e("code",[this._v("if")]),this._v(" 和 "),e("code",[this._v("if let")]),this._v(" 控制流的分支")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("enum")]),this._v(" - 定义一个枚举")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("extern")]),this._v(" - 引入一个外部 "),e("strong",[this._v("crate")]),this._v(" 、函数或变量")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("false")]),this._v(" - 布尔值 "),e("code",[this._v("false")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("fn")]),this._v(" - 定义一个函数或 "),e("strong",[this._v("函数指针类型")]),this._v(" ("),e("em",[this._v("function pointer type")]),this._v(")")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("for")]),this._v(" - 遍历一个迭代器或实现一个 "),e("strong",[this._v("trait")]),this._v("或者指定一个具体的生命周期")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("if")]),this._v(" - 基于条件表达式的结果分支")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("impl")]),this._v(" - 实现一个方法或 "),e("strong",[this._v("trait")]),this._v(" 功能")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("in")]),this._v(" - for循环语法的一部分")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("let")]),this._v(" - 绑定一个变量")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("loop")]),this._v(" - 无条件循环")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("match")]),this._v(" - 模式匹配")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("mod")]),this._v(" - 定义一个模块")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("move")]),this._v(" - 使闭包获取所有权")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("mut")]),this._v(" - 表示一个可变绑定")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("pub")]),this._v(" - 在结构体、"),e("code",[this._v("impl")]),this._v("块或模块中表示可以被外部使用")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("ref")]),this._v(" - 绑定一个引用")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("return")]),this._v(" - 从函数中返回")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("Self")]),this._v(" - 实现一个 "),e("strong",[this._v("trait")]),this._v(" 类型的类型别名")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("self")]),this._v(" - 表示方法本身或当前模块")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("static")]),this._v(" - 表示全局变量或在整个程序执行期间保持其生命周期")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("struct")]),this._v(" - 定义一个结构体")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("super")]),this._v(" - 表示当前模块的父模块")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("trait")]),this._v(" - 定义一个 "),e("strong",[this._v("trait")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("true")]),this._v(" - 布尔值 "),e("code",[this._v("true")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("type")]),this._v(" - 定义一个类型别名或相关联的类型")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("unsafe")]),this._v(" - 表示不安全的代码、函数、"),e("strong",[this._v("traits")]),this._v(" 或者方法实现")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("use")]),this._v(" - 引入外部空间的符号")])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("code",[this._v("while")]),this._v(" - 基于一个表达式的结果判断是否进行循环")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"未使用的保留字"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#未使用的保留字","aria-hidden":"true"}},[this._v("#")]),this._v(" 未使用的保留字")])},function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("ul",[i("li",[i("code",[t._v("abstract")])]),t._v(" "),i("li",[i("code",[t._v("alignof")])]),t._v(" "),i("li",[i("code",[t._v("become")])]),t._v(" "),i("li",[i("code",[t._v("box")])]),t._v(" "),i("li",[i("code",[t._v("do")])]),t._v(" "),i("li",[i("code",[t._v("final")])]),t._v(" "),i("li",[i("code",[t._v("macro")])]),t._v(" "),i("li",[i("code",[t._v("offsetof")])]),t._v(" "),i("li",[i("code",[t._v("override")])]),t._v(" "),i("li",[i("code",[t._v("priv")])]),t._v(" "),i("li",[i("code",[t._v("proc")])]),t._v(" "),i("li",[i("code",[t._v("pure")])]),t._v(" "),i("li",[i("code",[t._v("sizeof")])]),t._v(" "),i("li",[i("code",[t._v("typeof")])]),t._v(" "),i("li",[i("code",[t._v("unsized")])]),t._v(" "),i("li",[i("code",[t._v("virtual")])]),t._v(" "),i("li",[i("code",[t._v("yield")])])])}],!1,null,null,null);s.options.__file="appendix-01-keywords.md";e.default=s.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/60.867aed26.js b/src/.vuepress/dist/assets/js/60.867aed26.js
new file mode 100644
index 0000000..421a52e
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/60.867aed26.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[60],{210:function(t,e,s){"use strict";s.r(e);var n=s(0),a=Object(n.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch12-01-accepting-command-line-arguments.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch12-01-accepting-command-line-arguments.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._m(3),t._v(" "),t._m(4),s("p",[t._v("现在 "),s("code",[t._v("cargo new")]),t._v(" 生成的程序忽略任何传递给它的参数。"),s("a",{attrs:{href:"https://crates.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Crates.io"),s("OutboundLink")],1),t._v(" 上有一些现成的库可以帮助我们接受命令行参数,不过我们正在学习这些内容,让我们自己来实现一个。")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),s("p",[t._v("打印出参数 vector 中的值展示了程序可以访问指定为命令行参数的值。现在需要将这两个参数的值保存进变量这样就可以在程序的余下部分使用这些值了。让我们如示例 12-2 这样做:")]),t._v(" "),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),s("p",[t._v("好的,它可以工作!我们将所需的参数值保存进了对应的变量中。之后会增加一些错误处理来应对类似用户没有提供参数的情况,不过现在我们将忽略他们并开始增加读取文件功能。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"接受命令行参数"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#接受命令行参数","aria-hidden":"true"}},[this._v("#")]),this._v(" 接受命令行参数")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("一如既往使用 "),e("code",[this._v("cargo new")]),this._v(" 新建一个项目,我们称之为 "),e("code",[this._v("minigrep")]),this._v(" 以便与可能已经安装在系统上的 "),e("code",[this._v("grep")]),this._v(" 工具相区别:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo new minigrep\n Created binary (application) `minigrep` project\n$ cd minigrep\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("第一个任务是让 "),e("code",[this._v("minigrep")]),this._v(" 能够接受两个命令行参数:文件名和要搜索的字符串。也就是说我们希望能够使用 "),e("code",[this._v("cargo run")]),this._v("、要搜索的字符串和被搜索的文件的路径来运行程序,像这样:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo run searchstring example-filename.txt\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"读取参数值"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#读取参数值","aria-hidden":"true"}},[this._v("#")]),this._v(" 读取参数值")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("为了确保 "),s("code",[t._v("minigrep")]),t._v(" 能够获取传递给它的命令行参数的值,我们需要一个 Rust 标准库提供的函数,也就是 "),s("code",[t._v("std::env::args")]),t._v("。这个函数返回一个传递给程序的命令行参数的 "),s("strong",[t._v("迭代器")]),t._v("("),s("em",[t._v("iterator")]),t._v(")。我们还未讨论到迭代器(第十三章会全面的介绍他们),但是现在只需理解迭代器的两个细节:迭代器生成一系列的值,可以在迭代器上调用 "),s("code",[t._v("collect")]),t._v(" 方法将其转换为一个集合,比如包含所有迭代器产生元素的 vector。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("使用示例 12-1 中的代码来读取任何传递给 "),e("code",[this._v("minigrep")]),this._v(" 的命令行参数并将其收集到一个 vector 中。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("env"),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),s("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{attrs:{class:"token function"}},[t._v("main")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" args"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),s("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("String"),s("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),s("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" env"),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token punctuation"}},[t._v(":")]),s("span",{attrs:{class:"token function"}},[t._v("args")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{attrs:{class:"token function"}},[t._v("collect")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{attrs:{class:"token function"}},[t._v("println!")]),s("span",{attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{attrs:{class:"token string"}},[t._v('"{:?}"')]),s("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" args"),s("span",{attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 12-1:将命令行参数收集到一个 vector 中并打印出来")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("首先使用 "),s("code",[t._v("use")]),t._v(" 语句来将 "),s("code",[t._v("std::env")]),t._v(" 模块引入作用域以便可以使用它的 "),s("code",[t._v("args")]),t._v(" 函数。注意 "),s("code",[t._v("std::env::args")]),t._v(" 函数被嵌套进了两层模块中。正如第七章讲到的,当所需函数嵌套了多于一层模块时,通常将父模块引入作用域,而不是其自身。这便于我们利用 "),s("code",[t._v("std::env")]),t._v(" 中的其他函数。这比增加了 "),s("code",[t._v("use std::env::args;")]),t._v(" 后仅仅使用 "),s("code",[t._v("args")]),t._v(" 调用函数要更明确一些,因为 "),s("code",[t._v("args")]),t._v(" 容易被错认成一个定义于当前模块的函数。")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("blockquote",[s("h3",{attrs:{id:"args-函数和无效的-unicode"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#args-函数和无效的-unicode","aria-hidden":"true"}},[t._v("#")]),t._v(" "),s("code",[t._v("args")]),t._v(" 函数和无效的 Unicode")]),t._v(" "),s("p",[t._v("注意 "),s("code",[t._v("std::env::args")]),t._v(" 在其任何参数包含无效 Unicode 字符时会 panic。如果你需要接受包含无效 Unicode 字符的参数,使用 "),s("code",[t._v("std::env::args_os")]),t._v(" 代替。这个函数返回 "),s("code",[t._v("OsString")]),t._v(" 值而不是 "),s("code",[t._v("String")]),t._v(" 值。这里出于简单考虑使用了 "),s("code",[t._v("std::env::args")]),t._v(",因为 "),s("code",[t._v("OsString")]),t._v(" 值每个平台都不一样而且比 "),s("code",[t._v("String")]),t._v(" 值处理起来更为复杂。")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("在 "),s("code",[t._v("main")]),t._v(" 函数的第一行,我们调用了 "),s("code",[t._v("env::args")]),t._v(",并立即使用 "),s("code",[t._v("collect")]),t._v(" 来创建了一个包含迭代器所有值的 vector。"),s("code",[t._v("collect")]),t._v(" 可以被用来创建很多类型的集合,所以这里显式注明 "),s("code",[t._v("args")]),t._v(" 的类型来指定我们需要一个字符串 vector。虽然在 Rust 中我们很少会需要注明类型,"),s("code",[t._v("collect")]),t._v(" 就是一个经常需要注明类型的函数,因为 Rust 不能推断出你想要什么类型的集合。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("最后,我们使用调试格式 "),e("code",[this._v(":?")]),this._v(" 打印出 vector。让我们尝试不用参数运行代码,接着用两个参数:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('$ cargo run\n--snip--\n["target/debug/minigrep"]\n\n$ cargo run needle haystack\n--snip--\n["target/debug/minigrep", "needle", "haystack"]\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("注意 vector 的第一个值是 "),e("code",[this._v('"target/debug/minigrep"')]),this._v(",它是我们二进制文件的名称。这与 C 中的参数列表的行为相匹配,让程序使用在执行时调用它们的名称。如果要在消息中打印它或者根据用于调用程序的命令行别名更改程序的行为,通常可以方便地访问程序名称,不过考虑到本章的目的,我们将忽略它并只保存所需的两个参数。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"将参数值保存进变量"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#将参数值保存进变量","aria-hidden":"true"}},[this._v("#")]),this._v(" 将参数值保存进变量")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,should_panic extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('use std::env;\n\nfn main() {\n let args: Vec = env::args().collect();\n\n let query = &args[1];\n let filename = &args[2];\n\n println!("Searching for {}", query);\n println!("In file {}", filename);\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 12-2:创建变量来存放查询参数和文件名参数")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("正如之前打印出 vector 时所所看到的,程序的名称占据了 vector 的第一个值 "),s("code",[t._v("args[0]")]),t._v(",所以我们从索引 "),s("code",[t._v("1")]),t._v(" 开始。"),s("code",[t._v("minigrep")]),t._v(" 获取的第一个参数是需要搜索的字符串,所以将其将第一个参数的引用存放在变量 "),s("code",[t._v("query")]),t._v(" 中。第二个参数将是文件名,所以将第二个参数的引用放入变量 "),s("code",[t._v("filename")]),t._v(" 中。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("我们将临时打印出这些变量的值来证明代码如我们期望的那样工作。使用参数 "),e("code",[this._v("test")]),this._v(" 和 "),e("code",[this._v("sample.txt")]),this._v(" 再次运行这个程序:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo run test sample.txt\n Compiling minigrep v0.1.0 (file:///projects/minigrep)\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/minigrep test sample.txt`\nSearching for test\nIn file sample.txt\n")])])])}],!1,null,null,null);a.options.__file="ch12-01-accepting-command-line-arguments.md";e.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/61.74327fa7.js b/src/.vuepress/dist/assets/js/61.74327fa7.js
new file mode 100644
index 0000000..d730eec
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/61.74327fa7.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[61],{209:function(e,t,n){"use strict";n.r(t);var s=n(0),i=Object(s.a)({},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"content"},[e._m(0),e._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch12-02-reading-a-file.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ch12-02-reading-a-file.md"),n("OutboundLink")],1),e._v(" "),n("br"),e._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),e._v(" "),e._m(1),e._v(" "),e._m(2),e._v(" "),e._m(3),e._m(4),e._v(" "),e._m(5),e._v(" "),e._m(6),e._v(" "),e._m(7),e._m(8),e._v(" "),e._m(9),e._v(" "),e._m(10),e._v(" "),e._m(11),e._v(" "),e._m(12),e._v(" "),e._m(13),e._m(14)])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"读取文件"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#读取文件","aria-hidden":"true"}},[this._v("#")]),this._v(" 读取文件")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("现在我们要增加读取由 "),t("code",[this._v("filename")]),this._v(" 命令行参数指定的文件的功能。首先,需要一个用来测试的示例文件:用来确保 "),t("code",[this._v("minigrep")]),this._v(" 正常工作的最好的文件是拥有多行少量文本且有一些重复单词的文件。示例 12-3 是一首艾米莉·狄金森(Emily Dickinson)的诗,它正适合这个工作!在项目根目录创建一个文件 "),t("code",[this._v("poem.txt")]),this._v(',并输入诗 "I\'m nobody! Who are you?":')])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: poem.txt")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("I'm nobody! Who are you?\nAre you nobody, too?\nThen there's a pair of us - don't tell!\nThey'd banish us, you know.\n\nHow dreary to be somebody!\nHow public, like a frog\nTo tell your name the livelong day\nTo an admiring bog!\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-3:艾米莉·狄金森的诗 “I’m nobody! Who are you?”,一个好的测试用例")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("创建完这个文件之后,修改 "),t("em",[this._v("src/main.rs")]),this._v(" 并增加如示例 12-4 所示的打开文件的代码:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,should_panic extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('use std::env;\nuse std::fs;\n\nfn main() {\n# let args: Vec = env::args().collect();\n#\n# let query = &args[1];\n# let filename = &args[2];\n#\n# println!("Searching for {}", query);\n // --snip--\n println!("In file {}", filename);\n\n let contents = fs::read_to_string(filename)\n .expect("Something went wrong reading the file");\n\n println!("With text:\\n{}", contents);\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-4:读取第二个参数所指定的文件内容")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("首先,我们增加了更多的 "),t("code",[this._v("use")]),this._v(" 语句来引入标准库中的相关部分:需要 "),t("code",[this._v("std::fs")]),this._v(" 来处理文件。")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("在 "),n("code",[e._v("main")]),e._v(" 中新增了一行语句:"),n("code",[e._v("fs::read_to_string")]),e._v(" 接受 "),n("code",[e._v("filename")]),e._v(",打开文件,接着返回包含其内容的 "),n("code",[e._v("Result")]),e._v("。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("在这些代码之后,我们再次增加了临时的 "),t("code",[this._v("println!")]),this._v(" 打印出读取文件后 "),t("code",[this._v("contents")]),this._v(" 的值,这样就可以检查目前为止的程序能否工作。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("尝试运行这些代码,随意指定一个字符串作为第一个命令行参数(因为还未实现搜索功能的部分)而将 "),t("em",[this._v("poem.txt")]),this._v(" 文件将作为第二个参数:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo run the poem.txt\n Compiling minigrep v0.1.0 (file:///projects/minigrep)\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/minigrep the poem.txt`\nSearching for the\nIn file poem.txt\nWith text:\nI’m nobody! Who are you?\nAre you nobody, too?\nThen there’s a pair of us — don’t tell!\nThey’d banish us, you know.\n\nHow dreary to be somebody!\nHow public, like a frog\nTo tell your name the livelong day\nTo an admiring bog!\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("好的!代码读取并打印出了文件的内容。虽然它还有一些瑕疵:"),t("code",[this._v("main")]),this._v(" 函数有着多个职能,通常函数只负责一个功能的话会更简洁并易于维护。另一个问题是没有尽可能的处理错误。虽然我们的程序还很小,这些瑕疵并不是什么大问题,不过随着程序功能的丰富,将会越来越难以用简单的方法修复他们。在开发程序时,及早开始重构是一个最佳实践,因为重构少量代码时要容易的多,所以让我们现在就开始吧。")])}],!1,null,null,null);i.options.__file="ch12-02-reading-a-file.md";t.default=i.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/62.177c0b30.js b/src/.vuepress/dist/assets/js/62.177c0b30.js
new file mode 100644
index 0000000..2bc8d7a
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/62.177c0b30.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[62],{208:function(e,t,n){"use strict";n.r(t);var _=n(0),s=Object(_.a)({},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"content"},[e._m(0),e._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch12-03-improving-error-handling-and-modularity.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ch12-03-improving-error-handling-and-modularity.md"),n("OutboundLink")],1),e._v(" "),n("br"),e._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),e._v(" "),n("p",[e._v("为了改善我们的程序这里有四个问题需要修复,而且他们都与程序的组织方式和如何处理潜在错误有关。")]),e._v(" "),e._m(1),e._v(" "),e._m(2),e._v(" "),e._m(3),e._v(" "),e._m(4),e._v(" "),n("p",[e._v("让我们通过重构项目来解决这些问题。")]),e._v(" "),e._m(5),e._v(" "),e._m(6),e._v(" "),e._m(7),e._v(" "),e._m(8),e._v(" "),e._m(9),e._v(" "),e._m(10),e._v(" "),e._m(11),e._v(" "),e._m(12),e._m(13),e._v(" "),e._m(14),e._v(" "),n("p",[e._v("这对重构我们这小程序可能有点大材小用,不过我们将采用小的、增量的步骤进行重构。在做出这些改变之后,再次运行程序并验证参数解析是否仍然正常。经常验证你的进展是一个好习惯,这样在遇到问题时能帮助你定位问题的成因。")]),e._v(" "),e._m(15),e._v(" "),n("p",[e._v("我们可以采取另一个小的步骤来进一步改善这个函数。现在函数返回一个元组,不过立刻又将元组拆成了独立的部分。这是一个我们可能没有进行正确抽象的信号。")]),e._v(" "),e._m(16),e._v(" "),e._m(17),e._v(" "),e._m(18),e._v(" "),e._m(19),e._v(" "),e._m(20),e._m(21),e._v(" "),e._m(22),e._v(" "),e._m(23),e._v(" "),e._m(24),e._v(" "),e._m(25),e._v(" "),e._m(26),e._v(" "),e._m(27),e._v(" "),e._m(28),e._v(" "),e._m(29),e._v(" "),e._m(30),e._v(" "),e._m(31),e._m(32),e._v(" "),e._m(33),e._v(" "),e._m(34),e._v(" "),e._m(35),e._v(" "),e._m(36),e._m(37),e._v(" "),e._m(38),e._v(" "),e._m(39),e._v(" "),e._m(40),e._v(" "),e._m(41),e._m(42),e._v(" "),e._m(43),e._v(" "),e._m(44),e._v(" "),e._m(45),e._m(46),e._v(" "),e._m(47),e._v(" "),e._m(48),e._v(" "),e._m(49),e._v(" "),e._m(50),e._v(" "),e._m(51),e._m(52),e._v(" "),e._m(53),e._v(" "),e._m(54),e._v(" "),e._m(55),e._v(" "),e._m(56),e._v(" "),e._m(57),e._v(" "),e._m(58),e._v(" "),e._m(59),e._m(60),e._v(" "),e._m(61),e._v(" "),e._m(62),e._v(" "),e._m(63),n("p",[e._v("非常好!现在输出对于用户来说就友好多了。")]),e._v(" "),e._m(64),e._v(" "),e._m(65),e._v(" "),e._m(66),e._v(" "),e._m(67),e._v(" "),e._m(68),e._m(69),e._v(" "),e._m(70),e._v(" "),e._m(71),e._v(" "),e._m(72),e._v(" "),e._m(73),e._v(" "),e._m(74),e._m(75),e._v(" "),e._m(76),e._v(" "),e._m(77),e._v(" "),e._m(78),e._v(" "),e._m(79),e._v(" "),n("p",[e._v("上述代码能够编译,不过会有一个警告:")]),e._v(" "),e._m(80),e._m(81),e._v(" "),e._m(82),e._v(" "),e._m(83),e._v(" "),e._m(84),e._v(" "),e._m(85),e._m(86),e._v(" "),e._m(87),e._v(" "),e._m(88),e._v(" "),e._m(89),e._v(" "),e._m(90),e._v(" "),e._m(91),e._v(" "),e._m(92),e._v(" "),e._m(93),e._v(" "),e._m(94),e._m(95),e._v(" "),e._m(96),e._v(" "),e._m(97),e._v(" "),e._m(98),e._v(" "),e._m(99),e._m(100),e._v(" "),e._m(101),e._v(" "),e._m(102),e._v(" "),n("p",[e._v("让我们利用这些新创建的模块的优势来进行一些在旧代码中难以展开的工作,他们在新代码中却很简单:编写测试!")])])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"重构改进模块性和错误处理"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#重构改进模块性和错误处理","aria-hidden":"true"}},[this._v("#")]),this._v(" 重构改进模块性和错误处理")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("第一,"),t("code",[this._v("main")]),this._v(" 现在进行了两个任务:它解析了参数并打开了文件。对于一个这样的小函数,这并不是一个大问题。然而如果 "),t("code",[this._v("main")]),this._v(" 中的功能持续增加,"),t("code",[this._v("main")]),this._v(" 函数处理的独立任务也会增加。当函数承担了更多责任,它就更难以推导,更难以测试,并且更难以在不破坏其他部分的情况下做出修改。最好能分离出功能以便每个函数就负责一个任务。")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("这同时也关系到第二个问题:"),n("code",[e._v("search")]),e._v(" 和 "),n("code",[e._v("filename")]),e._v(" 是程序中的配置变量,而像 "),n("code",[e._v("f")]),e._v(" 和 "),n("code",[e._v("contents")]),e._v(" 则用来执行程序逻辑。随着 "),n("code",[e._v("main")]),e._v(" 函数的增长,就需要引入更多的变量到作用域中,而当作用域中有更多的变量时,将更难以追踪每个变量的目的。最好能将配置变量组织进一个结构,这样就能使他们的目的更明确了。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("第三个问题是如果打开文件失败我们使用 "),t("code",[this._v("expect")]),this._v(" 来打印出错误信息,不过这个错误信息只是说 "),t("code",[this._v("file not found")]),this._v("。除了缺少文件之外还有很多打开文件可能失败的方式:例如,文件可能存在,不过可能没有打开它的权限。如果我们现在就出于这种情况,打印出的 "),t("code",[this._v("file not found")]),this._v(" 错误信息就给了用户错误的建议!")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("第四,我们不停的使用 "),t("code",[this._v("expect")]),this._v(" 来处理不同的错误,如果用户没有指定足够的参数来运行程序,他们会从 Rust 得到 "),t("code",[this._v("index out of bounds")]),this._v(" 错误,而这并不能明确的解释问题。如果所有的错误处理都位于一处这样将来的维护者在需要修改错误处理逻辑时就只需要考虑这一处代码。将所有的错误处理都放在一处也有助于确保我们打印的错误信息对终端用户来说是有意义的。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"二进制项目的关注分离"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#二进制项目的关注分离","aria-hidden":"true"}},[this._v("#")]),this._v(" 二进制项目的关注分离")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("code",[this._v("main")]),this._v(" 函数负责多个任务的组织问题在许多二进制项目中很常见。所以 Rust 社区开发出一类在 "),t("code",[this._v("main")]),this._v(" 函数开始变得庞大时进行二进制程序的关注分离的指导性过程。这些过程有如下步骤:")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("ol",[n("li",[n("p",[e._v("将程序拆分成 "),n("em",[e._v("main.rs")]),e._v(" 和 "),n("em",[e._v("lib.rs")]),e._v(" 并将程序的逻辑放入 "),n("em",[e._v("lib.rs")]),e._v(" 中。")])]),e._v(" "),n("li",[n("p",[e._v("当命令行解析逻辑比较小时,可以保留在 "),n("em",[e._v("main.rs")]),e._v(" 中。")])]),e._v(" "),n("li",[n("p",[e._v("当命令行解析开始变得复杂时,也同样将其从 "),n("em",[e._v("main.rs")]),e._v(" 提取到 "),n("em",[e._v("lib.rs")]),e._v(" 中。")])]),e._v(" "),n("li",[n("p",[e._v("经过这些过程之后保留在 "),n("code",[e._v("main")]),e._v(" 函数中的责任应该被限制为:")]),e._v(" "),n("ul",[n("li",[e._v("使用参数值调用命令行解析逻辑")]),e._v(" "),n("li",[e._v("设置任何其他的配置")]),e._v(" "),n("li",[e._v("调用 "),n("em",[e._v("lib.rs")]),e._v(" 中的 "),n("code",[e._v("run")]),e._v(" 函数")]),e._v(" "),n("li",[e._v("如果 "),n("code",[e._v("run")]),e._v(" 返回错误,则处理这个错误")])])])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("这个模式的一切就是为了关注分离:"),n("em",[e._v("main.rs")]),e._v(" 处理程序运行,而 "),n("em",[e._v("lib.rs")]),e._v(" 处理所有的真正的任务逻辑。因为不能直接测试 "),n("code",[e._v("main")]),e._v(" 函数,这个结构通过将所有的程序逻辑移动到 "),n("em",[e._v("lib.rs")]),e._v(" 的函数中使得我们可以测试他们。仅仅保留在 "),n("em",[e._v("main.rs")]),e._v(" 中的代码将足够小以便阅读就可以验证其正确性。让我们遵循这些步骤来重构程序。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"提取参数解析器"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#提取参数解析器","aria-hidden":"true"}},[this._v("#")]),this._v(" 提取参数解析器")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("首先,我们将解析参数的功能提取到一个 "),n("code",[e._v("main")]),e._v(" 将会调用的函数中,为将命令行解析逻辑移动到 "),n("em",[e._v("src/lib.rs")]),e._v(" 中做准备。示例 12-5 中展示了新 "),n("code",[e._v("main")]),e._v(" 函数的开头,它调用了新函数 "),n("code",[e._v("parse_config")]),e._v("。目前它仍将定义在 "),n("em",[e._v("src/main.rs")]),e._v(" 中:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("fn main() {\n let args: Vec = env::args().collect();\n\n let (query, filename) = parse_config(&args);\n\n // --snip--\n}\n\nfn parse_config(args: &[String]) -> (&str, &str) {\n let query = &args[1];\n let filename = &args[2];\n\n (query, filename)\n}\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-5:从 "),t("code",[this._v("main")]),this._v(" 中提取出 "),t("code",[this._v("parse_config")]),this._v(" 函数")])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("我们仍然将命令行参数收集进一个 vector,不过不同于在 "),n("code",[e._v("main")]),e._v(" 函数中将索引 1 的参数值赋值给变量 "),n("code",[e._v("query")]),e._v(" 和将索引 2 的值赋值给变量 "),n("code",[e._v("filename")]),e._v(",我们将整个 vector 传递给 "),n("code",[e._v("parse_config")]),e._v(" 函数。接着 "),n("code",[e._v("parse_config")]),e._v(" 函数将包含决定哪个参数该放入哪个变量的逻辑,并将这些值返回到 "),n("code",[e._v("main")]),e._v("。仍然在 "),n("code",[e._v("main")]),e._v(" 中创建变量 "),n("code",[e._v("query")]),e._v(" 和 "),n("code",[e._v("filename")]),e._v(",不过 "),n("code",[e._v("main")]),e._v(" 不再负责处理命令行参数与变量如何对应。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"组合配置值"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#组合配置值","aria-hidden":"true"}},[this._v("#")]),this._v(" 组合配置值")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("另一个表明还有改进空间的迹象是 "),t("code",[this._v("parse_config")]),this._v(" 名称的 "),t("code",[this._v("config")]),this._v(" 部分,它暗示了我们返回的两个值是相关的并都是一个配置值的一部分。目前除了将这两个值组合进元组之外并没有表达这个数据结构的意义:我们可以将这两个值放入一个结构体并给每个字段一个有意义的名字。这会让未来的维护者更容易理解不同的值如何相互关联以及他们的目的。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("blockquote",[t("p",[this._v("注意:一些同学将这种在复杂类型更为合适的场景下使用基本类型的反模式称为 "),t("strong",[this._v("基本类型偏执")]),this._v("("),t("em",[this._v("primitive obsession")]),this._v(")。")])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("示例 12-6 展示了新定义的结构体 "),n("code",[e._v("Config")]),e._v(",它有字段 "),n("code",[e._v("query")]),e._v(" 和 "),n("code",[e._v("filename")]),e._v("。我们也改变了 "),n("code",[e._v("parse_config")]),e._v(" 函数来返回一个 "),n("code",[e._v("Config")]),e._v(" 结构体的实例,并更新 "),n("code",[e._v("main")]),e._v(" 来使用结构体字段而不是单独的变量:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,should_panic extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('# use std::env;\n# use std::fs;\n#\nfn main() {\n let args: Vec = env::args().collect();\n\n let config = parse_config(&args);\n\n println!("Searching for {}", config.query);\n println!("In file {}", config.filename);\n\n let contents = fs::read_to_string(config.filename)\n .expect("Something went wrong reading the file");\n\n // --snip--\n}\n\nstruct Config {\n query: String,\n filename: String,\n}\n\nfn parse_config(args: &[String]) -> Config {\n let query = args[1].clone();\n let filename = args[2].clone();\n\n Config { query, filename }\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-6:重构 "),t("code",[this._v("parse_config")]),this._v(" 返回一个 "),t("code",[this._v("Config")]),this._v(" 结构体实例")])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[n("code",[e._v("parse_config")]),e._v(" 的签名表明它现在返回一个 "),n("code",[e._v("Config")]),e._v(" 值。在 "),n("code",[e._v("parse_config")]),e._v(" 的函数体中,之前返回引用了 "),n("code",[e._v("args")]),e._v(" 中 "),n("code",[e._v("String")]),e._v(" 值的字符串 slice,现在我们选择定义 "),n("code",[e._v("Config")]),e._v(" 来包含拥有所有权的 "),n("code",[e._v("String")]),e._v(" 值。"),n("code",[e._v("main")]),e._v(" 中的 "),n("code",[e._v("args")]),e._v(" 变量是参数值的所有者并只允许 "),n("code",[e._v("parse_config")]),e._v(" 函数借用他们,这意味着如果 "),n("code",[e._v("Config")]),e._v(" 尝试获取 "),n("code",[e._v("args")]),e._v(" 中值的所有权将违反 Rust 的借用规则。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("还有许多不同的方式可以处理 "),t("code",[this._v("String")]),this._v(" 的数据,而最简单但有些不太高效的方式是调用这些值的 "),t("code",[this._v("clone")]),this._v(" 方法。这会生成 "),t("code",[this._v("Config")]),this._v(" 实例可以拥有的数据的完整拷贝,不过会比储存字符串数据的引用消耗更多的时间和内存。不过拷贝数据使得代码显得更加直白因为无需管理引用的生命周期,所以在这种情况下牺牲一小部分性能来换取简洁性的取舍是值得的。")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("blockquote",[n("h4",{attrs:{id:"使用-clone-的权衡取舍"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#使用-clone-的权衡取舍","aria-hidden":"true"}},[e._v("#")]),e._v(" 使用 "),n("code",[e._v("clone")]),e._v(" 的权衡取舍")]),e._v(" "),n("p",[e._v("由于其运行时消耗,许多 Rustacean 之间有一个趋势是倾向于避免使用 "),n("code",[e._v("clone")]),e._v(" 来解决所有权问题。在关于迭代器的第十三章中,我们将会学习如何更有效率的处理这种情况,不过现在,复制一些字符串来取得进展是没有问题的,因为只会进行一次这样的拷贝,而且文件名和要搜索的字符串都比较短。在第一轮编写时拥有一个可以工作但有点低效的程序要比尝试过度优化代码更好一些。随着你对 Rust 更加熟练,将能更轻松的直奔合适的方法,不过现在调用 "),n("code",[e._v("clone")]),e._v(" 是完全可以接受的。")])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("我们更新 "),n("code",[e._v("main")]),e._v(" 将 "),n("code",[e._v("parse_config")]),e._v(" 返回的 "),n("code",[e._v("Config")]),e._v(" 实例放入变量 "),n("code",[e._v("config")]),e._v(" 中,并将之前分别使用 "),n("code",[e._v("search")]),e._v(" 和 "),n("code",[e._v("filename")]),e._v(" 变量的代码更新为现在的使用 "),n("code",[e._v("Config")]),e._v(" 结构体的字段的代码。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("现在代码更明确的表现了我们的意图,"),t("code",[this._v("query")]),this._v(" 和 "),t("code",[this._v("filename")]),this._v(" 是相关联的并且他们的目的是配置程序如何工作。任何使用这些值的代码就知道在 "),t("code",[this._v("config")]),this._v(" 实例中对应目的的字段名中寻找他们。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"创建一个-config-构造函数"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#创建一个-config-构造函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 创建一个 "),t("code",[this._v("Config")]),this._v(" 构造函数")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("目前为止,我们将负责解析命令行参数的逻辑从 "),n("code",[e._v("main")]),e._v(" 提取到了 "),n("code",[e._v("parse_config")]),e._v(" 函数中,这有助于我们看清值 "),n("code",[e._v("query")]),e._v(" 和 "),n("code",[e._v("filename")]),e._v(" 是相互关联的并应该在代码中表现这种关系。接着我们增加了 "),n("code",[e._v("Config")]),e._v(" 结构体来描述 "),n("code",[e._v("query")]),e._v(" 和 "),n("code",[e._v("filename")]),e._v(" 的相关性,并能够从 "),n("code",[e._v("parse_config")]),e._v(" 函数中将这些值的名称作为结构体字段名称返回。")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("所以现在 "),n("code",[e._v("parse_config")]),e._v(" 函数的目的是创建一个 "),n("code",[e._v("Config")]),e._v(" 实例,我们可以将 "),n("code",[e._v("parse_config")]),e._v(" 从一个普通函数变为一个叫做 "),n("code",[e._v("new")]),e._v(" 的与结构体关联的函数。做出这个改变使得代码更符合习惯:可以像标准库中的 "),n("code",[e._v("String")]),e._v(" 调用 "),n("code",[e._v("String::new")]),e._v(" 来创建一个该类型的实例那样,将 "),n("code",[e._v("parse_config")]),e._v(" 变为一个与 "),n("code",[e._v("Config")]),e._v(" 关联的 "),n("code",[e._v("new")]),e._v(" 函数。示例 12-7 展示了需要做出的修改:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,should_panic extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("# use std::env;\n#\nfn main() {\n let args: Vec = env::args().collect();\n\n let config = Config::new(&args);\n\n // --snip--\n}\n\n# struct Config {\n# query: String,\n# filename: String,\n# }\n#\n// --snip--\n\nimpl Config {\n fn new(args: &[String]) -> Config {\n let query = args[1].clone();\n let filename = args[2].clone();\n\n Config { query, filename }\n }\n}\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-7:将 "),t("code",[this._v("parse_config")]),this._v(" 变为 "),t("code",[this._v("Config::new")])])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("这里将 "),n("code",[e._v("main")]),e._v(" 中调用 "),n("code",[e._v("parse_config")]),e._v(" 的地方更新为调用 "),n("code",[e._v("Config::new")]),e._v("。我们将 "),n("code",[e._v("parse_config")]),e._v(" 的名字改为 "),n("code",[e._v("new")]),e._v(" 并将其移动到 "),n("code",[e._v("impl")]),e._v(" 块中,这使得 "),n("code",[e._v("new")]),e._v(" 函数与 "),n("code",[e._v("Config")]),e._v(" 相关联。再次尝试编译并确保它可以工作。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"修复错误处理"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#修复错误处理","aria-hidden":"true"}},[this._v("#")]),this._v(" 修复错误处理")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("现在我们开始修复错误处理。回忆一下之前提到过如果 "),t("code",[this._v("args")]),this._v(" vector 包含少于 3 个项并尝试访问 vector 中索引 "),t("code",[this._v("1")]),this._v(" 或索引 "),t("code",[this._v("2")]),this._v(" 的值会造成程序 panic。尝试不带任何参数运行程序;这将看起来像这样:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo run\n Compiling minigrep v0.1.0 (file:///projects/minigrep)\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/minigrep`\nthread 'main' panicked at 'index out of bounds: the len is 1\nbut the index is 1', src/main.rs:29:21\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("code",[this._v("index out of bounds: the len is 1 but the index is 1")]),this._v(" 是一个针对程序员的错误信息,然而这并不能真正帮助终端用户理解发生了什么和他们应该做什么。现在就让我们修复它吧。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h4",{attrs:{id:"改善错误信息"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#改善错误信息","aria-hidden":"true"}},[this._v("#")]),this._v(" 改善错误信息")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("在示例 12-8 中,在 "),n("code",[e._v("new")]),e._v(" 函数中增加了一个检查在访问索引 "),n("code",[e._v("1")]),e._v(" 和 "),n("code",[e._v("2")]),e._v(" 之前检查 slice 是否足够长。如果 slice 不够长,我们使用一个更好的错误信息 panic 而不是 "),n("code",[e._v("index out of bounds")]),e._v(" 信息:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('// --snip--\nfn new(args: &[String]) -> Config {\n if args.len() < 3 {\n panic!("not enough arguments");\n }\n // --snip--\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-8:增加一个参数数量检查")])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("这类似于示例 9-9 中的 "),n("code",[e._v("Guess::new")]),e._v(" 函数,那里如果 "),n("code",[e._v("value")]),e._v(" 参数超出了有效值的范围就调用 "),n("code",[e._v("panic!")]),e._v("。不同于检查值的范围,这里检查 "),n("code",[e._v("args")]),e._v(" 的长度至少是 "),n("code",[e._v("3")]),e._v(",而函数的剩余部分则可以在假设这个条件成立的基础上运行。如果\n"),n("code",[e._v("args")]),e._v(" 少于 3 个项,则这个条件将为真,并调用 "),n("code",[e._v("panic!")]),e._v(" 立即终止程序。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("有了 "),t("code",[this._v("new")]),this._v(" 中这几行额外的代码,再次不带任何参数运行程序并看看现在错误看起来像什么:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo run\n Compiling minigrep v0.1.0 (file:///projects/minigrep)\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/minigrep`\nthread 'main' panicked at 'not enough arguments', src/main.rs:30:12\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("这个输出就好多了,现在有了一个合理的错误信息。然而,还是有一堆额外的信息我们不希望提供给用户。所以在这里使用示例 9-9 中的技术可能不是最好的;正如第九章所讲到的一样,"),t("code",[this._v("panic!")]),this._v(" 的调用更趋向于程序上的问题而不是使用上的问题。相反我们可以使用第九章学习的另一个技术 —— 返回一个可以表明成功或错误的 "),t("code",[this._v("Result")]),this._v("。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h4",{attrs:{id:"从-new-中返回-result-而不是调用-panic"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#从-new-中返回-result-而不是调用-panic","aria-hidden":"true"}},[this._v("#")]),this._v(" 从 "),t("code",[this._v("new")]),this._v(" 中返回 "),t("code",[this._v("Result")]),this._v(" 而不是调用 "),t("code",[this._v("panic!")])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("我们可以选择返回一个 "),n("code",[e._v("Result")]),e._v(" 值,它在成功时会包含一个 "),n("code",[e._v("Config")]),e._v(" 的实例,而在错误时会描述问题。当 "),n("code",[e._v("Config::new")]),e._v(" 与 "),n("code",[e._v("main")]),e._v(" 交流时,可以使用 "),n("code",[e._v("Result")]),e._v(" 类型来表明这里存在问题。接着修改 "),n("code",[e._v("main")]),e._v(" 将 "),n("code",[e._v("Err")]),e._v(" 成员转换为对用户更友好的错误,而不是 "),n("code",[e._v("panic!")]),e._v(" 调用产生的关于 "),n("code",[e._v("thread 'main'")]),e._v(" 和 "),n("code",[e._v("RUST_BACKTRACE")]),e._v(" 的文本。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("示例 12-9 展示了为了返回 "),t("code",[this._v("Result")]),this._v(" 在 "),t("code",[this._v("Config::new")]),this._v(" 的返回值和函数体中所需的改变。注意这还不能编译,直到下一个示例同时也更新了 "),t("code",[this._v("main")]),this._v(" 之后。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('impl Config {\n fn new(args: &[String]) -> Result {\n if args.len() < 3 {\n return Err("not enough arguments");\n }\n\n let query = args[1].clone();\n let filename = args[2].clone();\n\n Ok(Config { query, filename })\n }\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-9:从 "),t("code",[this._v("Config::new")]),this._v(" 中返回 "),t("code",[this._v("Result")])])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("现在 "),n("code",[e._v("new")]),e._v(" 函数返回一个 "),n("code",[e._v("Result")]),e._v(",在成功时带有一个 "),n("code",[e._v("Config")]),e._v(" 实例而在出现错误时带有一个 "),n("code",[e._v("&'static str")]),e._v("。回忆一下第十章 “静态生命周期” 中讲到 "),n("code",[e._v("&'static str")]),e._v(" 是字符串字面值的类型,也是目前的错误信息。")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[n("code",[e._v("new")]),e._v(" 函数体中有两处修改:当没有足够参数时不再调用 "),n("code",[e._v("panic!")]),e._v(",而是返回 "),n("code",[e._v("Err")]),e._v(" 值。同时我们将 "),n("code",[e._v("Config")]),e._v(" 返回值包装进 "),n("code",[e._v("Ok")]),e._v(" 成员中。这些修改使得函数符合其新的类型签名。")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("通过让 "),n("code",[e._v("Config::new")]),e._v(" 返回一个 "),n("code",[e._v("Err")]),e._v(" 值,这就允许 "),n("code",[e._v("main")]),e._v(" 函数处理 "),n("code",[e._v("new")]),e._v(" 函数返回的 "),n("code",[e._v("Result")]),e._v(" 值并在出现错误的情况更明确的结束进程。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h4",{attrs:{id:"config-new-调用并处理错误"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#config-new-调用并处理错误","aria-hidden":"true"}},[this._v("#")]),this._v(" "),t("code",[this._v("Config::new")]),this._v(" 调用并处理错误")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("为了处理错误情况并打印一个对用户友好的信息,我们需要像示例 12-10 那样更新 "),n("code",[e._v("main")]),e._v(" 函数来处理现在 "),n("code",[e._v("Config::new")]),e._v(" 返回的 "),n("code",[e._v("Result")]),e._v("。另外还需要负责手动实现 "),n("code",[e._v("panic!")]),e._v(" 的使用非零错误码退出命令行工具的工作。非零的退出状态是一个告诉调用程序的进程我们的程序以错误状态退出的惯例信号。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('use std::process;\n\nfn main() {\n let args: Vec = env::args().collect();\n\n let config = Config::new(&args).unwrap_or_else(|err| {\n println!("Problem parsing arguments: {}", err);\n process::exit(1);\n });\n\n // --snip--\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-10:如果新建 "),t("code",[this._v("Config")]),this._v(" 失败则使用错误码退出")])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("在上面的示例中,使用了一个之前没有涉及到的方法:"),n("code",[e._v("unwrap_or_else")]),e._v(",它定义于标准库的 "),n("code",[e._v("Result")]),e._v(" 上。使用 "),n("code",[e._v("unwrap_or_else")]),e._v(" 可以进行一些自定义的非 "),n("code",[e._v("panic!")]),e._v(" 的错误处理。当 "),n("code",[e._v("Result")]),e._v(" 是 "),n("code",[e._v("Ok")]),e._v(" 时,这个方法的行为类似于 "),n("code",[e._v("unwrap")]),e._v(":它返回 "),n("code",[e._v("Ok")]),e._v(" 内部封装的值。然而,当其值是 "),n("code",[e._v("Err")]),e._v(" 时,该方法会调用一个 "),n("strong",[e._v("闭包")]),e._v("("),n("em",[e._v("closure")]),e._v("),也就是一个我们定义的作为参数传递给 "),n("code",[e._v("unwrap_or_else")]),e._v(" 的匿名函数。第十三章会更详细的介绍闭包。现在你需要理解的是 "),n("code",[e._v("unwrap_or_else")]),e._v(" 会将 "),n("code",[e._v("Err")]),e._v(" 的内部值,也就是示例 12-9 中增加的 "),n("code",[e._v("not enough arguments")]),e._v(" 静态字符串的情况,传递给闭包中位于两道竖线间的参数 "),n("code",[e._v("err")]),e._v("。闭包中的代码在其运行时可以使用这个 "),n("code",[e._v("err")]),e._v(" 值。")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("我们新增了一个 "),n("code",[e._v("use")]),e._v(" 行来从标准库中导入 "),n("code",[e._v("process")]),e._v("。在错误的情况闭包中将被运行的代码只有两行:我们打印出了 "),n("code",[e._v("err")]),e._v(" 值,接着调用了 "),n("code",[e._v("std::process::exit")]),e._v("。"),n("code",[e._v("process::exit")]),e._v(" 会立即停止程序并将传递给它的数字作为退出状态码。这类似于示例 12-8 中使用的基于 "),n("code",[e._v("panic!")]),e._v(" 的错误处理,除了不会再得到所有的额外输出了。让我们试试:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo run\n Compiling minigrep v0.1.0 (file:///projects/minigrep)\n Finished dev [unoptimized + debuginfo] target(s) in 0.48 secs\n Running `target/debug/minigrep`\nProblem parsing arguments: not enough arguments\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"从-main-提取逻辑"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#从-main-提取逻辑","aria-hidden":"true"}},[this._v("#")]),this._v(" 从 "),t("code",[this._v("main")]),this._v(" 提取逻辑")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("现在我们完成了配置解析的重构:让我们转向程序的逻辑。正如 “二进制项目的关注分离” 部分所展开的讨论,我们将提取一个叫做 "),t("code",[this._v("run")]),this._v(" 的函数来存放目前 "),t("code",[this._v("main")]),this._v("函数中不属于设置配置或处理错误的所有逻辑。一旦完成这些,"),t("code",[this._v("main")]),this._v(" 函数将简明的足以通过观察来验证,而我们将能够为所有其他逻辑编写测试。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("示例 12-11 展示了提取出来的 "),t("code",[this._v("run")]),this._v(" 函数。目前我们只进行小的增量式的提取函数的改进。我们仍将在 "),t("em",[this._v("src/main.rs")]),this._v(" 中定义这个函数:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('fn main() {\n // --snip--\n\n println!("Searching for {}", config.query);\n println!("In file {}", config.filename);\n\n run(config);\n}\n\nfn run(config: Config) {\n let contents = fs::read_to_string(config.filename)\n .expect("something went wrong reading the file");\n\n println!("With text:\\n{}", contents);\n}\n\n// --snip--\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-11:提取 "),t("code",[this._v("run")]),this._v(" 函数来包含剩余的程序逻辑")])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("现在 "),n("code",[e._v("run")]),e._v(" 函数包含了 "),n("code",[e._v("main")]),e._v(" 中从读取文件开始的剩余的所有逻辑。"),n("code",[e._v("run")]),e._v(" 函数获取一个 "),n("code",[e._v("Config")]),e._v(" 实例作为参数。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h4",{attrs:{id:"从-run-函数中返回错误"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#从-run-函数中返回错误","aria-hidden":"true"}},[this._v("#")]),this._v(" 从 "),t("code",[this._v("run")]),this._v(" 函数中返回错误")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("通过将剩余的逻辑分离进 "),n("code",[e._v("run")]),e._v(" 函数而不是留在 "),n("code",[e._v("main")]),e._v(" 中,就可以像示例 12-9 中的 "),n("code",[e._v("Config::new")]),e._v(" 那样改进错误处理。不再通过 "),n("code",[e._v("expect")]),e._v(" 允许程序 panic,"),n("code",[e._v("run")]),e._v(" 函数将会在出错时返回一个 "),n("code",[e._v("Result")]),e._v("。这让我们进一步以一种对用户友好的方式统一 "),n("code",[e._v("main")]),e._v(" 中的错误处理。示例 12-12 展示了 "),n("code",[e._v("run")]),e._v(" 签名和函数体中的改变:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('use std::error::Error;\n\n// --snip--\n\nfn run(config: Config) -> Result<(), Box> {\n let contents = fs::read_to_string(config.filename)?;\n\n println!("With text:\\n{}", contents);\n\n Ok(())\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-12:修改 "),t("code",[this._v("run")]),this._v(" 函数返回 "),t("code",[this._v("Result")])])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("这里我们做出了三个明显的修改。首先,将 "),n("code",[e._v("run")]),e._v(" 函数的返回类型变为 "),n("code",[e._v("Result<(), Box>")]),e._v("。之前这个函数返回 unit 类型 "),n("code",[e._v("()")]),e._v(",现在它仍然保持作为 "),n("code",[e._v("Ok")]),e._v(" 时的返回值。")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("对于错误类型,使用了 "),n("strong",[e._v("trait 对象")]),e._v(" "),n("code",[e._v("Box")]),e._v("(在开头使用了 "),n("code",[e._v("use")]),e._v(" 语句将 "),n("code",[e._v("std::error::Error")]),e._v(" 引入作用域)。第十七章会涉及 trait 对象。目前只需知道 "),n("code",[e._v("Box")]),e._v(" 意味着函数会返回实现了 "),n("code",[e._v("Error")]),e._v(" trait 的类型,不过无需指定具体将会返回的值的类型。这提供了在不同的错误场景可能有不同类型的错误返回值的灵活性。这也就是 "),n("code",[e._v("dyn")]),e._v(",它是 “动态”(“dynamic”)的缩写。")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("第二个改变是去掉了 "),n("code",[e._v("expect")]),e._v(" 调用并替换为第九章讲到的 "),n("code",[e._v("?")]),e._v("。不同于遇到错误就 "),n("code",[e._v("panic!")]),e._v(","),n("code",[e._v("?")]),e._v(" 会从函数中返回错误值并让调用者来处理它。")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("第三个修改是现在成功时这个函数会返回一个 "),n("code",[e._v("Ok")]),e._v(" 值。因为 "),n("code",[e._v("run")]),e._v(" 函数签名中声明成功类型返回值是 "),n("code",[e._v("()")]),e._v(",这意味着需要将 unit 类型值包装进 "),n("code",[e._v("Ok")]),e._v(" 值中。"),n("code",[e._v("Ok(())")]),e._v(" 一开始看起来有点奇怪,不过这样使用 "),n("code",[e._v("()")]),e._v(" 是表明我们调用 "),n("code",[e._v("run")]),e._v(" 只是为了它的副作用的惯用方式;它并没有返回什么有意义的值。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("warning: unused `std::result::Result` which must be used\n --\x3e src/main.rs:18:5\n |\n18 | run(config);\n | ^^^^^^^^^^^^\n= note: #[warn(unused_must_use)] on by default\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("Rust 提示我们的代码忽略了 "),t("code",[this._v("Result")]),this._v(" 值,它可能表明这里存在一个错误。虽然我们没有检查这里是否有一个错误,而编译器提醒我们这里应该有一些错误处理代码!现在就让我们修正这个问题。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h4",{attrs:{id:"处理-main-中-run-返回的错误"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#处理-main-中-run-返回的错误","aria-hidden":"true"}},[this._v("#")]),this._v(" 处理 "),t("code",[this._v("main")]),this._v(" 中 "),t("code",[this._v("run")]),this._v(" 返回的错误")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("我们将检查错误并使用类似示例 12-10 中 "),t("code",[this._v("Config::new")]),this._v(" 处理错误的技术来处理他们,不过有一些细微的不同:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('fn main() {\n // --snip--\n\n println!("Searching for {}", config.query);\n println!("In file {}", config.filename);\n\n if let Err(e) = run(config) {\n println!("Application error: {}", e);\n\n process::exit(1);\n }\n}\n')])])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("我们使用 "),n("code",[e._v("if let")]),e._v(" 来检查 "),n("code",[e._v("run")]),e._v(" 是否返回一个 "),n("code",[e._v("Err")]),e._v(" 值,不同于 "),n("code",[e._v("unwrap_or_else")]),e._v(",并在出错时调用 "),n("code",[e._v("process::exit(1)")]),e._v("。"),n("code",[e._v("run")]),e._v(" 并不返回像 "),n("code",[e._v("Config::new")]),e._v(" 返回的 "),n("code",[e._v("Config")]),e._v(" 实例那样需要 "),n("code",[e._v("unwrap")]),e._v(" 的值。因为 "),n("code",[e._v("run")]),e._v(" 在成功时返回 "),n("code",[e._v("()")]),e._v(",而我们只关心检测错误,所以并不需要 "),n("code",[e._v("unwrap_or_else")]),e._v(" 来返回未封装的值,因为它只会是 "),n("code",[e._v("()")]),e._v("。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("不过两个例子中 "),t("code",[this._v("if let")]),this._v(" 和 "),t("code",[this._v("unwrap_or_else")]),this._v(" 的函数体都一样:打印出错误并退出。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"将代码拆分到库-crate"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#将代码拆分到库-crate","aria-hidden":"true"}},[this._v("#")]),this._v(" 将代码拆分到库 crate")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("现在我们的 "),n("code",[e._v("minigrep")]),e._v(" 项目看起来好多了!现在我们将要拆分 "),n("em",[e._v("src/main.rs")]),e._v(" 并将一些代码放入 "),n("em",[e._v("src/lib.rs")]),e._v(",这样就能测试他们并拥有一个含有更少功能的 "),n("code",[e._v("main")]),e._v(" 函数。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("让我们将所有不是 "),t("code",[this._v("main")]),this._v(" 函数的代码从 "),t("em",[this._v("src/main.rs")]),this._v(" 移动到新文件 "),t("em",[this._v("src/lib.rs")]),this._v(" 中:")])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("ul",[n("li",[n("code",[e._v("run")]),e._v(" 函数定义")]),e._v(" "),n("li",[e._v("相关的 "),n("code",[e._v("use")]),e._v(" 语句")]),e._v(" "),n("li",[n("code",[e._v("Config")]),e._v(" 的定义")]),e._v(" "),n("li",[n("code",[e._v("Config::new")]),e._v(" 函数定义")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("现在 "),t("em",[this._v("src/lib.rs")]),this._v(" 的内容应该看起来像示例 12-13(为了简洁省略了函数体)。注意直到下一个示例修改完 "),t("em",[this._v("src/main.rs")]),this._v(" 之后,代码还不能编译:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("use std::error::Error;\nuse std::fs;\n\npub struct Config {\n pub query: String,\n pub filename: String,\n}\n\nimpl Config {\n pub fn new(args: &[String]) -> Result {\n // --snip--\n }\n}\n\npub fn run(config: Config) -> Result<(), Box> {\n // --snip--\n}\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-13:将 "),t("code",[this._v("Config")]),this._v(" 和 "),t("code",[this._v("run")]),this._v(" 移动到 "),t("em",[this._v("src/lib.rs")])])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("这里使用了公有的 "),n("code",[e._v("pub")]),e._v(" 关键字:在 "),n("code",[e._v("Config")]),e._v("、其字段和其 "),n("code",[e._v("new")]),e._v(" 方法,以及 "),n("code",[e._v("run")]),e._v(" 函数上。现在我们有了一个拥有可以测试的公有 API 的库 crate 了。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("现在需要在 "),t("em",[this._v("src/main.rs")]),this._v(" 中将移动到 "),t("em",[this._v("src/lib.rs")]),this._v(" 的代码引入二进制 crate 的作用域中,如示例 12-14 所示:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("Filename: src/main.rs")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-rust,ignore extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("use std::env;\nuse std::process;\n\nuse minigrep;\nuse minigrep::Config;\n\nfn main() {\n // --snip--\n if let Err(e) = minigrep::run(config) {\n // --snip--\n }\n}\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"caption"},[this._v("示例 12-14:将 "),t("code",[this._v("minigrep")]),this._v(" crate 引入 "),t("em",[this._v("src/main.rs")]),this._v(" 的作用域中")])])},function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("p",[e._v("为了将库 crate 引入二进制 crate,我们使用 "),n("code",[e._v("extern crate minigrep")]),e._v("。接着增加 "),n("code",[e._v("use minigrep::Config")]),e._v(" 将 "),n("code",[e._v("Config")]),e._v(" 类型引入作用域,并使用 crate 名作为 "),n("code",[e._v("run")]),e._v(" 函数的前缀。通过这些重构,所有功能应该能够联系在一起并运行了。运行 "),n("code",[e._v("cargo run")]),e._v(" 来确保一切都正确的衔接在一起。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("哇哦!这可有很多的工作,不过我们为将来的成功打下了基础。现在处理错误将更容易,同时代码也更加模块化。从现在开始几乎所有的工作都将在 "),t("em",[this._v("src/lib.rs")]),this._v(" 中进行。")])}],!1,null,null,null);s.options.__file="ch12-03-improving-error-handling-and-modularity.md";t.default=s.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/63.3e0d51da.js b/src/.vuepress/dist/assets/js/63.3e0d51da.js
new file mode 100644
index 0000000..66c5cbc
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/63.3e0d51da.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[63],{207:function(t,s,e){"use strict";e.r(s);var n=e(0),a=Object(n.a)({},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[t._m(0),t._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch12-04-testing-the-librarys-functionality.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch12-04-testing-the-librarys-functionality.md"),e("OutboundLink")],1),t._v(" "),e("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),e("p",[t._v("这只是众多编写软件的方法之一,不过 TDD 有助于驱动代码的设计。在编写能使测试通过的代码之前编写测试有助于在开发过程中保持高测试覆盖率。")]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),e("p",[t._v("如果尝试不用生命周期编译的话,我们将得到如下错误:")]),t._v(" "),t._m(17),t._m(18),t._v(" "),e("p",[t._v("其他语言中并不需要你在函数签名中将参数与返回值相关联。所以这么做可能仍然感觉有些陌生,随着时间的推移这将会变得越来越容易。你可能想要将这个例子与第十章中生命 “生命周期与引用有效性” 部分做对比。")]),t._v(" "),e("p",[t._v("现在运行测试:")]),t._v(" "),t._m(19),e("p",[t._v("好的,测试失败了,这正是我们所期望的。修改代码来让测试通过吧!")]),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),e("p",[t._v("让我们一步一步的来,从遍历每行开始。")]),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),e("p",[t._v("测试通过了,它可以工作了!")]),t._v(" "),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),t._m(46),t._v(" "),e("p",[t._v("现在整个程序应该可以工作了!让我们试一试,首先使用一个只会在艾米莉·狄金森的诗中返回一行的单词 “frog”:")]),t._v(" "),t._m(47),e("p",[t._v("好的!现在试试一个会匹配多行的单词,比如 “body”:")]),t._v(" "),t._m(48),e("p",[t._v('最后,让我们确保搜索一个在诗中哪里都没有的单词时不会得到任何行,比如 "monomorphization":')]),t._v(" "),t._m(49),e("p",[t._v("非常好!我们创建了一个属于自己的迷你版经典工具,并学习了很多如何组织程序的知识。我们还学习了一些文件输入输出、生命周期、测试和命令行解析的内容。")]),t._v(" "),e("p",[t._v("为了使这个项目更丰满,我们将简要的展示如何处理环境变量和打印到标准错误,这两者在编写命令行程序时都很有用。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"采用测试驱动开发完善库的功能"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#采用测试驱动开发完善库的功能","aria-hidden":"true"}},[this._v("#")]),this._v(" 采用测试驱动开发完善库的功能")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("现在我们将逻辑提取到了 "),e("em",[t._v("src/lib.rs")]),t._v(" 并将所有的参数解析和错误处理留在了 "),e("em",[t._v("src/main.rs")]),t._v(" 中,为代码的核心功能编写测试将更加容易。我们可以直接使用多种参数调用函数并检查返回值而无需从命令行运行二进制文件了。如果你愿意的话,请自行为 "),e("code",[t._v("Config::new")]),t._v(" 和 "),e("code",[t._v("run")]),t._v(" 函数的功能编写一些测试。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在这一部分,我们将遵循测试驱动开发(Test Driven Development, TDD)的模式来逐步增加 "),s("code",[this._v("minigrep")]),this._v(" 的搜索逻辑。这是一个软件开发技术,它遵循如下步骤:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ol",[s("li",[this._v("编写一个会失败的测试,并运行它以确保其因为你期望的原因失败。")]),this._v(" "),s("li",[this._v("编写或修改刚好足够的代码来使得新的测试通过。")]),this._v(" "),s("li",[this._v("重构刚刚增加或修改的代码,并确保测试仍然能通过。")]),this._v(" "),s("li",[this._v("从步骤 1 开始重复!")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们将测试驱动实现实际在文件内容中搜索查询字符串并返回匹配的行示例的功能。我们将在一个叫做 "),s("code",[this._v("search")]),this._v(" 的函数中增加这些功能。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"编写失败测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#编写失败测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 编写失败测试")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("去掉 "),e("em",[t._v("src/lib.rs")]),t._v(" 和 "),e("em",[t._v("src/main.rs")]),t._v(" 中用于检查程序行为的 "),e("code",[t._v("println!")]),t._v(" 语句,因为不再真正需要他们了。接着我们会像第十一章那样增加一个 "),e("code",[t._v("test")]),t._v(" 模块和一个测试函数。测试函数指定了 "),e("code",[t._v("search")]),t._v(" 函数期望拥有的行为:它会获取一个需要查询的字符串和用来查询的文本,并只会返回包含请求的文本行。示例 12-15 展示了这个测试,它还不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[t._v("# "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" search"),e("span",{attrs:{class:"token operator"}},[t._v("<")]),e("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),e("span",{attrs:{class:"token operator"}},[t._v(">")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{attrs:{class:"token operator"}},[t._v("&")]),e("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Vec"),e("span",{attrs:{class:"token operator"}},[t._v("<")]),e("span",{attrs:{class:"token operator"}},[t._v("&")]),e("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),e("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),e("span",{attrs:{class:"token function"}},[t._v("vec!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n# "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),e("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),e("span",{attrs:{class:"token keyword"}},[t._v("super")]),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),e("span",{attrs:{class:"token operator"}},[t._v("*")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),e("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),e("span",{attrs:{class:"token function"}},[t._v("one_result")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" query "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{attrs:{class:"token string"}},[t._v('"duct"')]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" contents "),e("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(' "\\\nRust'),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\nsafe"),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fast"),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" productive"),e("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\nPick three"),e("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v('"'),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),e("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("vec!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{attrs:{class:"token string"}},[t._v('"safe, fast, productive."')]),e("span",{attrs:{class:"token punctuation"}},[t._v("]")]),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("search")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 12-15:创建一个我们期望的 "),s("code",[this._v("search")]),this._v(" 函数的失败测试")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里选择使用 "),s("code",[this._v('"duct"')]),this._v(" 作为这个测试中需要搜索的字符串。用来搜索的文本有三行,其中只有一行包含 "),s("code",[this._v('"duct"')]),this._v("。我们断言 "),s("code",[this._v("search")]),this._v(" 函数的返回值只包含期望的那一行。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们还不能运行这个测试并看到它失败,因为它甚至都还不能编译:"),s("code",[this._v("search")]),this._v(" 函数还不存在呢!我们将增加足够的代码来使其能够编译:一个总是会返回空 vector 的 "),s("code",[this._v("search")]),this._v(" 函数定义,如示例 12-16 所示。然后这个测试应该能够编译并因为空 vector 并不匹配一个包含一行 "),s("code",[this._v('"safe, fast, productive."')]),this._v(" 的 vector 而失败。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-rust extra-class"},[e("pre",{pre:!0,attrs:{class:"language-rust"}},[e("code",[e("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" search"),e("span",{attrs:{class:"token operator"}},[t._v("<")]),e("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),e("span",{attrs:{class:"token operator"}},[t._v(">")]),e("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),e("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),e("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),e("span",{attrs:{class:"token operator"}},[t._v("&")]),e("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),e("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Vec"),e("span",{attrs:{class:"token operator"}},[t._v("<")]),e("span",{attrs:{class:"token operator"}},[t._v("&")]),e("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),e("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),e("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{attrs:{class:"token function"}},[t._v("vec!")]),e("span",{attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),e("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 12-16:刚好足够使测试通过编译的 "),s("code",[this._v("search")]),this._v(" 函数定义")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("注意需要在 "),e("code",[t._v("search")]),t._v(" 的签名中定义一个显式生命周期 "),e("code",[t._v("'a")]),t._v(" 并用于 "),e("code",[t._v("contents")]),t._v(" 参数和返回值。回忆一下第十章中讲到生命周期参数指定哪个参数的生命周期与返回值的生命周期相关联。在这个例子中,我们表明返回的 vector 中应该包含引用参数 "),e("code",[t._v("contents")]),t._v("(而不是参数"),e("code",[t._v("query")]),t._v(") slice 的字符串 slice。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("换句话说,我们告诉 Rust 函数 "),e("code",[t._v("search")]),t._v(" 返回的数据将与 "),e("code",[t._v("search")]),t._v(" 函数中的参数 "),e("code",[t._v("contents")]),t._v(" 的数据存在的一样久。这是非常重要的!为了使这个引用有效那么 "),e("strong",[t._v("被")]),t._v(" slice 引用的数据也需要保持有效;如果编译器认为我们是在创建 "),e("code",[t._v("query")]),t._v(" 而不是 "),e("code",[t._v("contents")]),t._v(" 的字符串 slice,那么安全检查将是不正确的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0106]: missing lifetime specifier\n --\x3e src/lib.rs:5:51\n |\n5 | fn search(query: &str, contents: &str) -> Vec<&str> {\n | ^ expected lifetime\nparameter\n |\n = help: this function's return type contains a borrowed value, but the\n signature does not say whether it is borrowed from `query` or `contents`\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 不可能知道我们需要的是哪一个参数,所以需要告诉它。因为参数 "),s("code",[this._v("contents")]),this._v(" 包含了所有的文本而且我们希望返回匹配的那部分文本,所以我们知道 "),s("code",[this._v("contents")]),this._v(" 是应该要使用生命周期语法来与返回值相关联的参数。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test\n Compiling minigrep v0.1.0 (file:///projects/minigrep)\n--warnings--\n Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs\n Running target/debug/deps/minigrep-abcabcabc\n\nrunning 1 test\ntest tests::one_result ... FAILED\n\nfailures:\n\n---- tests::one_result stdout ----\n thread 'tests::one_result' panicked at 'assertion failed: `(left ==\nright)`\nleft: `[\"safe, fast, productive.\"]`,\nright: `[]`)', src/lib.rs:48:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n\n\nfailures:\n tests::one_result\n\ntest result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out\n\nerror: test failed, to rerun pass '--lib'\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"编写使测试通过的代码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#编写使测试通过的代码","aria-hidden":"true"}},[this._v("#")]),this._v(" 编写使测试通过的代码")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("目前测试之所以会失败是因为我们总是返回一个空的 vector。为了修复并实现 "),s("code",[this._v("search")]),this._v(",我们的程序需要遵循如下步骤:")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("ul",[e("li",[t._v("遍历内容的每一行文本。")]),t._v(" "),e("li",[t._v("查看这一行是否包含要搜索的字符串。")]),t._v(" "),e("li",[t._v("如果有,将这一行加入列表返回值中。")]),t._v(" "),e("li",[t._v("如果没有,什么也不做。")]),t._v(" "),e("li",[t._v("返回匹配到的结果列表")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用-lines-方法遍历每一行"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-lines-方法遍历每一行","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("lines")]),this._v(" 方法遍历每一行")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 有一个有助于一行一行遍历字符串的方法,出于方便它被命名为 "),s("code",[this._v("lines")]),this._v(",它如示例 12-17 这样工作。注意这还不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {\n for line in contents.lines() {\n // do something with line\n }\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 12-17:遍历 "),s("code",[this._v("contents")]),this._v(" 的每一行")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("lines")]),this._v(" 方法返回一个迭代器。第十三章会深入了解迭代器,不过我们已经在示例 3-5 中见过使用迭代器的方法了,在那里使用了一个 "),s("code",[this._v("for")]),this._v(" 循环和迭代器在一个集合的每一项上运行了一些代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"用查询字符串搜索每一行"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#用查询字符串搜索每一行","aria-hidden":"true"}},[this._v("#")]),this._v(" 用查询字符串搜索每一行")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接下来将会增加检查当前行是否包含查询字符串的功能。幸运的是,字符串类型为此也有一个叫做 "),s("code",[this._v("contains")]),this._v(" 的实用方法!如示例 12-18 所示在 "),s("code",[this._v("search")]),this._v(" 函数中加入 "),s("code",[this._v("contains")]),this._v(" 方法调用。注意这仍然不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {\n for line in contents.lines() {\n if line.contains(query) {\n // do something with line\n }\n }\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 12-18:增加检查文本行是否包含 "),s("code",[this._v("query")]),this._v(" 中字符串的功能")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"存储匹配的行"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#存储匹配的行","aria-hidden":"true"}},[this._v("#")]),this._v(" 存储匹配的行")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("我们还需要一个方法来存储包含查询字符串的行。为此可以在 "),e("code",[t._v("for")]),t._v(" 循环之前创建一个可变的 vector 并调用 "),e("code",[t._v("push")]),t._v(" 方法在 vector 中存放一个 "),e("code",[t._v("line")]),t._v("。在 "),e("code",[t._v("for")]),t._v(" 循环之后,返回这个 vector,如示例 12-19 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {\n let mut results = Vec::new();\n\n for line in contents.lines() {\n if line.contains(query) {\n results.push(line);\n }\n }\n\n results\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 12-19:储存匹配的行以便可以返回他们")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在 "),s("code",[this._v("search")]),this._v(" 函数应该返回只包含 "),s("code",[this._v("query")]),this._v(" 的那些行,而测试应该会通过。让我们运行测试:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo test\n--snip--\nrunning 1 test\ntest tests::one_result ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("到此为止,我们可以考虑一下重构 "),s("code",[this._v("search")]),this._v(" 的实现并时刻保持测试通过来保持其功能不变的机会了。"),s("code",[this._v("search")]),this._v(" 函数中的代码并不坏,不过并没有利用迭代器的一些实用功能。第十三章将回到这个例子并深入探索迭代器并看看如何改进代码。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"在-run-函数中使用-search-函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#在-run-函数中使用-search-函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 在 "),s("code",[this._v("run")]),this._v(" 函数中使用 "),s("code",[this._v("search")]),this._v(" 函数")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("p",[t._v("现在 "),e("code",[t._v("search")]),t._v(" 函数是可以工作并测试通过了的,我们需要实际在 "),e("code",[t._v("run")]),t._v(" 函数中调用 "),e("code",[t._v("search")]),t._v("。需要将 "),e("code",[t._v("config.query")]),t._v(" 值和 "),e("code",[t._v("run")]),t._v(" 从文件中读取的 "),e("code",[t._v("contents")]),t._v(" 传递给 "),e("code",[t._v("search")]),t._v(" 函数。接着 "),e("code",[t._v("run")]),t._v(" 会打印出 "),e("code",[t._v("search")]),t._v(" 返回的每一行:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('pub fn run(config: Config) -> Result<(), Box> {\n let contents = fs::read_to_string(config.filename)?;\n\n for line in search(&config.query, &contents) {\n println!("{}", line);\n }\n\n Ok(())\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里仍然使用了 "),s("code",[this._v("for")]),this._v(" 循环获取了 "),s("code",[this._v("search")]),this._v(" 返回的每一行并打印出来。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run frog poem.txt\n Compiling minigrep v0.1.0 (file:///projects/minigrep)\n Finished dev [unoptimized + debuginfo] target(s) in 0.38 secs\n Running `target/debug/minigrep frog poem.txt`\nHow public, like a frog\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run body poem.txt\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/minigrep body poem.txt`\nI’m nobody! Who are you?\nAre you nobody, too?\nHow dreary to be somebody!\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run monomorphization poem.txt\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/minigrep monomorphization poem.txt`\n")])])])}],!1,null,null,null);a.options.__file="ch12-04-testing-the-librarys-functionality.md";s.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/64.e8778b11.js b/src/.vuepress/dist/assets/js/64.e8778b11.js
new file mode 100644
index 0000000..91be1cb
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/64.e8778b11.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[64],{206:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch12-05-working-with-environment-variables.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch12-05-working-with-environment-variables.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),n("p",[t._v("让我们看看这个实现能否通过测试:")]),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._m(34),t._v(" "),n("p",[t._v("如果你使用 PowerShell,则需要用两句命令而不是一句来设置环境变量并运行程序:")]),t._v(" "),t._m(35),n("p",[t._v("这回应该得到包含可能有大写字母的 “to” 的行:")]),t._v(" "),t._m(36),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"处理环境变量"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#处理环境变量","aria-hidden":"true"}},[this._v("#")]),this._v(" 处理环境变量")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们将增加一个额外的功能来改进 "),s("code",[this._v("minigrep")]),this._v(":一个通过环境变量启用的大小写不敏感搜索的选项。可以将其设计为一个命令行参数并要求用户每次需要时都加上它,不过相反我们将使用环境变量。这允许用户设置环境变量一次之后在整个终端会话中所有的搜索都将是大小写不敏感的。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"编写一个大小写不敏感-search-函数的失败测试"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#编写一个大小写不敏感-search-函数的失败测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 编写一个大小写不敏感 "),s("code",[this._v("search")]),this._v(" 函数的失败测试")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们希望增加一个新函数 "),s("code",[this._v("search_case_insensitive")]),this._v(",并将会在设置了环境变量时调用它。这里将继续遵循 TDD 过程,其第一步是再次编写一个失败测试。我们将为新的大小写不敏感搜索函数新增一个测试函数,并将老的测试函数从 "),s("code",[this._v("one_result")]),this._v(" 改名为 "),s("code",[this._v("case_sensitive")]),this._v(" 来更清楚的表明这两个测试的区别,如示例 12-20 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("super")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("case_sensitive")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" query "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"duct"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" contents "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(' "\\\nRust'),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\nsafe"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fast"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" productive"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\nPick three"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\nDuct tape"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v('"'),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token string"}},[t._v('"safe, fast, productive."')]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("search")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("case_insensitive")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" query "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"rUsT"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" contents "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(' "\\\nRust'),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\nsafe"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fast"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" productive"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\nPick three"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\nTrust me"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v('"'),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token string"}},[t._v('"Rust:"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token string"}},[t._v('"Trust me."')]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("search_case_insensitive")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 12-20:为准备添加的大小写不敏感函数新增失败测试")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("注意我们也改变了老测试中 "),s("code",[this._v("contents")]),this._v(" 的值。还新增了一个含有文本 "),s("code",[this._v('"Duct tape."')]),this._v(' 的行,它有一个大写的 D,这在大小写敏感搜索时不应该匹配 "duct"。我们修改这个测试以确保不会意外破坏已经实现的大小写敏感搜索功能;这个测试现在应该能通过并在处理大小写不敏感搜索时应该能一直通过。')])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("大小写 "),n("strong",[t._v("不敏感")]),t._v(" 搜索的新测试使用 "),n("code",[t._v('"rUsT"')]),t._v(" 作为其查询字符串。在我们将要增加的 "),n("code",[t._v("search_case_insensitive")]),t._v(" 函数中,"),n("code",[t._v('"rUsT"')]),t._v(" 查询应该包含带有一个大写 R 的 "),n("code",[t._v('"Rust:"')]),t._v(" 还有 "),n("code",[t._v('"Trust me."')]),t._v(" 这两行,即便他们与查询的大小写都不同。这个测试现在会编译失败因为还没有定义 "),n("code",[t._v("search_case_insensitive")]),t._v(" 函数。请随意增加一个总是返回空 vector 的骨架实现,正如示例 12-16 中 "),n("code",[t._v("search")]),t._v(" 函数为了使测试编译并失败时所做的那样。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"实现-search-case-insensitive-函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#实现-search-case-insensitive-函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 实现 "),s("code",[this._v("search_case_insensitive")]),this._v(" 函数")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("search_case_insensitive")]),t._v(" 函数,如示例 12-21 所示,将与 "),n("code",[t._v("search")]),t._v(" 函数基本相同。唯一的区别是它会将 "),n("code",[t._v("query")]),t._v(" 变量和每一 "),n("code",[t._v("line")]),t._v(" 都变为小写,这样不管输入参数是大写还是小写,在检查该行是否包含查询字符串时都会是小写。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" search_case_insensitive"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" query "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" query"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("to_lowercase")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" results "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Vec"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" line "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("lines")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" line"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("to_lowercase")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("contains")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("query"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n results"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("push")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("line"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n results\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 12-21:定义 "),s("code",[this._v("search_case_insensitive")]),this._v(" 函数,它在比较查询和每一行之前将他们都转换为小写")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("首先我们将 "),n("code",[t._v("query")]),t._v(" 字符串转换为小写,并将其覆盖到同名的变量中。对查询字符串调用 "),n("code",[t._v("to_lowercase")]),t._v(" 是必需的,这样不管用户的查询是 "),n("code",[t._v('"rust"')]),t._v("、"),n("code",[t._v('"RUST"')]),t._v("、"),n("code",[t._v('"Rust"')]),t._v(" 或者 "),n("code",[t._v('"rUsT"')]),t._v(",我们都将其当作 "),n("code",[t._v('"rust"')]),t._v(" 处理并对大小写不敏感。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意 "),n("code",[t._v("query")]),t._v(" 现在是一个 "),n("code",[t._v("String")]),t._v(" 而不是字符串 slice,因为调用 "),n("code",[t._v("to_lowercase")]),t._v(" 是在创建新数据,而不是引用现有数据。如果查询字符串是 "),n("code",[t._v('"rUsT"')]),t._v(",这个字符串 slice 并不包含可供我们使用的小写的 "),n("code",[t._v("u")]),t._v(" 或 "),n("code",[t._v("t")]),t._v(",所以必需分配一个包含 "),n("code",[t._v('"rust"')]),t._v(" 的新 "),n("code",[t._v("String")]),t._v("。现在当我们将 "),n("code",[t._v("query")]),t._v(" 作为一个参数传递给 "),n("code",[t._v("contains")]),t._v(" 方法时,需要增加一个 & 因为 "),n("code",[t._v("contains")]),t._v(" 的签名被定义为获取一个字符串 slice。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("接下来在检查每个 "),n("code",[t._v("line")]),t._v(" 是否包含 "),n("code",[t._v("search")]),t._v(" 之前增加了一个 "),n("code",[t._v("to_lowercase")]),t._v(" 调用将他们都变为小写。现在我们将 "),n("code",[t._v("line")]),t._v(" 和 "),n("code",[t._v("query")]),t._v(" 都转换成了小写,这样就可以不管查询的大小写进行匹配了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("running 2 tests\ntest tests::case_insensitive ... ok\ntest tests::case_sensitive ... ok\n\ntest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("好的!现在,让我们在 "),s("code",[this._v("run")]),this._v(" 函数中实际调用新 "),s("code",[this._v("search_case_insensitive")]),this._v(" 函数。首先,我们将在 "),s("code",[this._v("Config")]),this._v(" 结构体中增加一个配置项来切换大小写敏感和大小写不敏感搜索。增加这些字段会导致编译错误,因为我们还没有在任何地方初始化这些字段:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Config "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" query"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" case_sensitive"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里增加了 "),n("code",[t._v("case_sensitive")]),t._v(" 字符来存放一个布尔值。接着我们需要 "),n("code",[t._v("run")]),t._v(" 函数检查 "),n("code",[t._v("case_sensitive")]),t._v(" 字段的值并使用它来决定是否调用 "),n("code",[t._v("search")]),t._v(" 函数或 "),n("code",[t._v("search_case_insensitive")]),t._v(" 函数,如示例 12-22 所示。注意这还不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("error"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Error"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" File"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("io"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("prelude"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token operator"}},[t._v("*")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" search"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" search_case_insensitive"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("query"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("str"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'a")]),t._v(" str"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Config "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# query"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# case_sensitive"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("run")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("config"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Config"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Box"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("dyn Error"),n("span",{attrs:{class:"token operator"}},[t._v(">>")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" contents "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" fs"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("read_to_string")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("config"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("?"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" results "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" config"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("case_sensitive "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("search")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("config"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("query"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("search_case_insensitive")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("config"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("query"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),t._v("contents"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" line "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" results "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"{}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" line"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 12-22:根据 "),s("code",[this._v("config.case_sensitive")]),this._v(" 的值调用 "),s("code",[this._v("search")]),this._v(" 或 "),s("code",[this._v("search_case_insensitive")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("最后需要实际检查环境变量。处理环境变量的函数位于标准库的 "),n("code",[t._v("env")]),t._v(" 模块中,所以我们需要在 "),n("em",[t._v("src/lib.rs")]),t._v(" 的开头增加一个 "),n("code",[t._v("use std::env;")]),t._v(" 行将这个模块引入作用域中。接着在 "),n("code",[t._v("Config::new")]),t._v(" 中使用 "),n("code",[t._v("env")]),t._v(" 模块的 "),n("code",[t._v("var")]),t._v(" 方法来检查一个叫做 "),n("code",[t._v("CASE_INSENSITIVE")]),t._v(" 的环境变量,如示例 12-23 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("env"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Config "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# query"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# case_sensitive"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token comment"}},[t._v("// --snip--")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Config "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("args"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("String"),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Config"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),t._v(" str"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" args"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("len")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"not enough arguments"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" query "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" args"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("clone")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" filename "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" args"),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("clone")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" case_sensitive "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" env"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("var")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"CASE_INSENSITIVE"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("is_err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Config "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" query"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" case_sensitive "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 12-23:检查叫做 "),s("code",[this._v("CASE_INSENSITIVE")]),this._v(" 的环境变量")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里创建了一个新变量 "),n("code",[t._v("case_sensitive")]),t._v("。为了设置它的值,需要调用 "),n("code",[t._v("env::var")]),t._v(" 函数并传递我们需要寻找的环境变量名称,"),n("code",[t._v("CASE_INSENSITIVE")]),t._v("。"),n("code",[t._v("env::var")]),t._v(" 返回一个 "),n("code",[t._v("Result")]),t._v(",它在环境变量被设置时返回包含其值的 "),n("code",[t._v("Ok")]),t._v(" 成员,并在环境变量未被设置时返回 "),n("code",[t._v("Err")]),t._v(" 成员。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们使用 "),n("code",[t._v("Result")]),t._v(" 的 "),n("code",[t._v("is_err")]),t._v(" 方法来检查其是否是一个 error(也就是环境变量未被设置的情况),这也就意味着我们 "),n("strong",[t._v("需要")]),t._v(" 进行一个大小写敏感搜索。如果"),n("code",[t._v("CASE_INSENSITIVE")]),t._v(" 环境变量被设置为任何值,"),n("code",[t._v("is_err")]),t._v(" 会返回 false 并将进行大小写不敏感搜索。我们并不关心环境变量所设置的 "),n("strong",[t._v("值")]),t._v(",只关心它是否被设置了,所以检查 "),n("code",[t._v("is_err")]),t._v(" 而不是 "),n("code",[t._v("unwrap")]),t._v("、"),n("code",[t._v("expect")]),t._v(" 或任何我们已经见过的 "),n("code",[t._v("Result")]),t._v(" 的方法。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们将变量 "),n("code",[t._v("case_sensitive")]),t._v(" 的值传递给 "),n("code",[t._v("Config")]),t._v(" 实例,这样 "),n("code",[t._v("run")]),t._v(" 函数可以读取其值并决定是否调用 "),n("code",[t._v("search")]),t._v(" 或者示例 12-22 中实现的 "),n("code",[t._v("search_case_insensitive")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("让我们试一试吧!首先不设置环境变量并使用查询 "),s("code",[this._v("to")]),this._v(" 运行程序,这应该会匹配任何全小写的单词 “to” 的行:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ cargo run to poem.txt\n Compiling minigrep v0.1.0 (file:///projects/minigrep)\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/minigrep to poem.txt`\nAre you nobody, too?\nHow dreary to be somebody!\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("看起来程序仍然能够工作!现在将 "),s("code",[this._v("CASE_INSENSITIVE")]),this._v(" 设置为 "),s("code",[this._v("1")]),this._v(" 并仍使用相同的查询 "),s("code",[this._v("to")]),this._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ $env:CASE_INSENSITIVE=1\n$ cargo run to poem.txt\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("$ CASE_INSENSITIVE=1 cargo run to poem.txt\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/minigrep to poem.txt`\nAre you nobody, too?\nHow dreary to be somebody!\nTo tell your name the livelong day\nTo an admiring bog!\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("好极了,我们也得到了包含 “To” 的行!现在 "),s("code",[this._v("minigrep")]),this._v(" 程序可以通过环境变量控制进行大小写不敏感搜索了。现在你知道了如何管理由命令行参数或环境变量设置的选项了!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("一些程序允许对相同配置同时使用参数 "),s("strong",[this._v("和")]),this._v(" 环境变量。在这种情况下,程序来决定参数和环境变量的优先级。作为一个留给你的测试,尝试通过一个命令行参数或一个环境变量来控制大小写不敏感搜索。并在运行程序时遇到矛盾值时决定命令行参数和环境变量的优先级。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("std::env")]),this._v(" 模块还包含了更多处理环境变量的实用功能;请查看官方文档来了解其可用的功能。")])}],!1,null,null,null);e.options.__file="ch12-05-working-with-environment-variables.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/65.b0dde85a.js b/src/.vuepress/dist/assets/js/65.b0dde85a.js
new file mode 100644
index 0000000..30ed4d1
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/65.b0dde85a.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[65],{205:function(t,e,s){"use strict";s.r(e);var r=s(0),n=Object(r.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch12-06-writing-to-stderr-instead-of-stdout.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch12-06-writing-to-stderr-instead-of-stdout.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),s("p",[t._v("命令行程序被期望将错误信息发送到标准错误流,这样即便选择将标准输出流重定向到文件中时仍然能看到错误信息。目前我们的程序并不符合期望;相反我们将看到它将错误信息输出保存到了文件中。")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),s("p",[t._v("是的,错误信息被打印到了标准输出中。像这样的错误信息被打印到标准错误中将有用的多,并在重定向标准输出时只将成功运行的信息写入文件。我们将改变他们。")]),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),s("p",[t._v("如果使用不会造成错误的参数再次运行程序,不过仍然将标准输出重定向到一个文件,像这样:")]),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),s("p",[t._v("这一部分展示了现在我们适当的使用了成功时产生的标准输出和错误时产生的标准错误。")]),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),s("p",[t._v("接下来,让我们探索一些 Rust 中受函数式编程语言影响的功能:闭包和迭代器。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"将错误信息输出到标准错误而不是标准输出"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#将错误信息输出到标准错误而不是标准输出","aria-hidden":"true"}},[this._v("#")]),this._v(" 将错误信息输出到标准错误而不是标准输出")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("目前为止,我们将所有的输出都 "),s("code",[t._v("println!")]),t._v(" 到了终端。大部分终端都提供了两种输出:"),s("strong",[t._v("标准输出")]),t._v("("),s("em",[t._v("standard output")]),t._v(","),s("code",[t._v("stdout")]),t._v(")对应通用信息,"),s("strong",[t._v("标准错误")]),t._v("("),s("em",[t._v("standard error")]),t._v(","),s("code",[t._v("stderr")]),t._v(")则用于错误信息。这种区别允许用户选择将程序正常输出定向到一个文件中并仍将错误信息打印到屏幕上。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("但是 "),e("code",[this._v("println!")]),this._v(" 函数只能够打印到标准输出,所以我们必需使用其他方法来打印到标准错误。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"检查错误应该写入何处"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#检查错误应该写入何处","aria-hidden":"true"}},[this._v("#")]),this._v(" 检查错误应该写入何处")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("首先,让我们观察一下目前 "),e("code",[this._v("minigrep")]),this._v(" 打印的所有内容都被写入了标准输出,包括应该被写入标准错误的错误信息。可以通过将标准输出流重定向到一个文件同时有意产生一个错误来做到这一点。我们没有重定向标准错误流,所以任何发送到标准错误的内容将会继续显示在屏幕上。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("展示这种行为的方式是通过 "),e("code",[this._v(">")]),this._v(" 和文件名 "),e("em",[this._v("output.txt")]),this._v(" 来与运行程序,这个文件是期望重定向标准输出流的位置。并不传递任何参数应该会产生一个错误:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo run > output.txt\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("code",[this._v(">")]),this._v(" 语法告诉 shell 将标准输出的内容写入到 "),e("em",[this._v("output.txt")]),this._v(" 文件中而不是屏幕上。我们并没有看到期望的错误信息打印到屏幕上,所以这意味着它一定被写入了文件中。如下是 "),e("em",[this._v("output.txt")]),this._v(" 所包含的:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("Problem parsing arguments: not enough arguments\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"将错误打印到标准错误"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#将错误打印到标准错误","aria-hidden":"true"}},[this._v("#")]),this._v(" 将错误打印到标准错误")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("让我们如示例 12-24 所示的代码改变错误信息是如何被打印的。得益于本章早些时候的重构,所有打印错误信息的代码都位于 "),s("code",[t._v("main")]),t._v(" 一个函数中。标准库提供了 "),s("code",[t._v("eprintln!")]),t._v(" 宏来打印到标准错误流,所以将两个调用 "),s("code",[t._v("println!")]),t._v(" 打印错误信息的位置替换为 "),s("code",[t._v("eprintln!")]),t._v(":")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('fn main() {\n let args: Vec = env::args().collect();\n\n let config = Config::new(&args).unwrap_or_else(|err| {\n eprintln!("Problem parsing arguments: {}", err);\n process::exit(1);\n });\n\n if let Err(e) = minigrep::run(config) {\n eprintln!("Application error: {}", e);\n\n process::exit(1);\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 12-24:使用 "),e("code",[this._v("eprintln!")]),this._v(" 将错误信息写入标准错误而不是标准输出")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("将 "),e("code",[this._v("println!")]),this._v(" 改为 "),e("code",[this._v("eprintln!")]),this._v(" 之后,让我们再次尝试用同样的方式运行程序,不使用任何参数并通过 "),e("code",[this._v(">")]),this._v(" 重定向标准输出:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo run > output.txt\nProblem parsing arguments: not enough arguments\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("现在我们看到了屏幕上的错误信息,同时 "),e("em",[this._v("output.txt")]),this._v(" 里什么也没有,这正是命令行程序所期望的行为。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo run to poem.txt > output.txt\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("我们并不会在终端看到任何输出,同时 "),e("code",[this._v("output.txt")]),this._v(" 将会包含其结果:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: output.txt")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("Are you nobody, too?\nHow dreary to be somebody!\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"总结"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("在这一章中,我们回顾了目前为止的一些主要章节并涉及了如何在 Rust 环境中进行常规的 I/O 操作。通过使用命令行参数、文件、环境变量和打印错误的 "),e("code",[this._v("eprintln!")]),this._v(" 宏,现在你已经准备好编写命令行程序了。通过结合前几章的知识,你的代码将会是组织良好的,并能有效的将数据存储到合适的数据结构中、更好的处理错误,并且还是经过良好测试的。")])}],!1,null,null,null);n.options.__file="ch12-06-writing-to-stderr-instead-of-stdout.md";e.default=n.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/66.3bba1132.js b/src/.vuepress/dist/assets/js/66.3bba1132.js
new file mode 100644
index 0000000..bc7ca59
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/66.3bba1132.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[66],{204:function(t,e,r){"use strict";r.r(e);var s=r(0),n=Object(s.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),r("blockquote",[r("p",[r("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch13-00-functional-features.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch13-00-functional-features.md"),r("OutboundLink")],1),t._v(" "),r("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),r("p",[t._v("本章我们不会讨论函数式编程是或不是什么的问题,而是展示 Rust 的一些在功能上与其他被认为是函数式语言类似的特性。")]),t._v(" "),r("p",[t._v("更具体的,我们将要涉及:")]),t._v(" "),t._m(2),t._v(" "),r("p",[t._v("还有其它受函数式风格影响的 Rust 功能,比如模式匹配和枚举,这些已经在其他章节中讲到过了。掌握闭包和迭代器则是编写符合语言风格的高性能 Rust 代码的重要一环,所以我们将专门用一整章来讲解他们。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"rust-中的函数式语言功能:迭代器与闭包"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#rust-中的函数式语言功能:迭代器与闭包","aria-hidden":"true"}},[this._v("#")]),this._v(" Rust 中的函数式语言功能:迭代器与闭包")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("Rust 的设计灵感来源于很多现存的语言和技术。其中一个显著的影响就是 "),e("strong",[this._v("函数式编程")]),this._v("("),e("em",[this._v("functional programming")]),this._v(")。函数式编程风格通常包含将函数作为参数值或其他函数的返回值、将函数赋值给变量以供之后执行等等。")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ul",[r("li",[r("strong",[t._v("闭包")]),t._v("("),r("em",[t._v("Closures")]),t._v("),一个可以储存在变量里的类似函数的结构")]),t._v(" "),r("li",[r("strong",[t._v("迭代器")]),t._v("("),r("em",[t._v("Iterators")]),t._v("),一种处理元素序列的方式")]),t._v(" "),r("li",[t._v("如何使用这些功能来改进第十二章的 I/O 项目。")]),t._v(" "),r("li",[t._v("这两个功能的性能。("),r("strong",[t._v("剧透警告:")]),t._v(" 他们的速度超乎你的想象!)")])])}],!1,null,null,null);n.options.__file="ch13-00-functional-features.md";e.default=n.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/67.fb2ec2e1.js b/src/.vuepress/dist/assets/js/67.fb2ec2e1.js
new file mode 100644
index 0000000..00ab849
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/67.fb2ec2e1.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[67],{203:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch13-01-closures.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch13-01-closures.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),n("p",[t._v("让我们来看一个存储稍后要执行的闭包的示例。其间我们会讨论闭包的语法、类型推断和 trait。")]),t._v(" "),n("p",[t._v("考虑一下这个假想的情况:我们在一个通过 app 生成自定义健身计划的初创企业工作。其后端使用 Rust 编写,而生成健身计划的算法需要考虑很多不同的因素,比如用户的年龄、身体质量指数(Body Mass Index)、用户喜好、最近的健身活动和用户指定的强度系数。本例中实际的算法并不重要,重要的是这个计算只花费几秒钟。我们只希望在需要时调用算法,并且只希望调用一次,这样就不会让用户等得太久。")]),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._m(6),t._v(" "),t._m(7),t._v(" "),n("p",[t._v("所需的输入有这些:")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),n("p",[t._v("如果用户需要高强度锻炼,这里有一些额外的逻辑:如果 app 生成的随机数刚好是 3,app 相反会建议用户稍做休息并补充水分。如果不是,则用户会从复杂算法中得到数分钟跑步的高强度锻炼计划。")]),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._v(" "),t._m(39),t._m(40),t._v(" "),n("p",[t._v("现在耗时的计算只在一个地方被调用,并只会在需要结果的时候执行改代码。")]),t._v(" "),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),n("p",[t._v("闭包通常很短并只与对应相对任意的场景较小的上下文中。在这些有限制的上下文中,编译器能可靠的推断参数和返回值的类型,类似于它是如何能够推断大部分变量的类型一样。")]),t._v(" "),n("p",[t._v("强制在这些小的匿名函数中注明类型是很恼人的,并且与编译器已知的信息存在大量的重复。")]),t._v(" "),n("p",[t._v("类似于变量,如果相比严格的必要性你更希望增加明确性并变得更啰嗦,可以选择增加类型注解;为示例 13-5 中定义的闭包标注类型将看起来像示例 13-7 中的定义:")]),t._v(" "),t._m(44),t._v(" "),t._m(45),t._m(46),t._v(" "),n("p",[t._v("有了类型注解闭包的语法就更类似函数了。如下是一个对其参数加一的函数的定义与拥有相同行为闭包语法的纵向对比。这里增加了一些空格来对齐相应部分。这展示了闭包语法如何类似于函数语法,除了使用竖线而不是括号以及几个可选的语法之外:")]),t._v(" "),t._m(47),n("p",[t._v("第一行展示了一个函数定义,而第二行展示了一个完整标注的闭包定义。第三行闭包定义中省略了类型注解,而第四行去掉了可选的大括号,因为闭包体只有一行。这些都是有效的闭包定义,并在调用时产生相同的行为。")]),t._v(" "),t._m(48),t._v(" "),t._m(49),t._v(" "),t._m(50),t._m(51),t._v(" "),n("p",[t._v("编译器给出如下错误:")]),t._v(" "),t._m(52),t._m(53),t._v(" "),t._m(54),t._v(" "),n("p",[t._v("回到我们的健身计划生成 app ,在示例 13-6 中的代码仍然调用了多于需要的慢计算闭包。解决这个问题的一个方法是在全部代码中的每一个需要多个慢计算闭包结果的地方,可以将结果保存进变量以供复用,这样就可以使用变量而不是再次调用闭包。但是这样就会有很多重复的保存结果变量的地方。")]),t._v(" "),t._m(55),t._v(" "),n("p",[t._v("为了让结构体存放闭包,我们需要能够指定闭包的类型,因为结构体定义需要知道其每一个字段的类型。每一个闭包实例有其自己独有的匿名类型:也就是说,即便两个闭包有着相同的签名,他们的类型仍然可以被认为是不同。为了定义使用闭包的结构体、枚举或函数参数,需要像第十章讨论的那样使用泛型和 trait bound。")]),t._v(" "),t._m(56),t._v(" "),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._v(" "),t._m(60),t._m(61),t._v(" "),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._v(" "),t._m(65),t._v(" "),t._m(66),t._v(" "),t._m(67),t._m(68),t._v(" "),t._m(69),t._v(" "),t._m(70),t._v(" "),t._m(71),t._v(" "),t._m(72),t._v(" "),t._m(73),t._v(" "),t._m(74),t._v(" "),t._m(75),t._m(76),t._v(" "),t._m(77),t._v(" "),t._m(78),t._v(" "),t._m(79),t._v(" "),t._m(80),t._v(" "),t._m(81),t._v(" "),t._m(82),t._m(83),t._v(" "),t._m(84),t._v(" "),t._m(85),t._m(86),t._v(" "),t._m(87),t._v(" "),t._m(88),t._v(" "),t._m(89),t._v(" "),n("p",[t._v("在健身计划生成器的例子中,我们只将闭包作为内联匿名函数来使用。不过闭包还有另一个函数所没有的功能:他们可以捕获其环境并访问其被定义的作用域的变量。")]),t._v(" "),t._m(90),t._v(" "),t._m(91),t._v(" "),t._m(92),t._m(93),t._v(" "),t._m(94),t._v(" "),n("p",[t._v("函数则不能做到同样的事,如果尝试如下例子,它并不能编译:")]),t._v(" "),t._m(95),t._v(" "),t._m(96),n("p",[t._v("这会得到一个错误:")]),t._v(" "),t._m(97),n("p",[t._v("编译器甚至会提示我们这只能用于闭包!")]),t._v(" "),n("p",[t._v("当闭包从环境中捕获一个值,闭包会在闭包体中储存这个值以供使用。这会使用内存并产生额外的开销,当执行不会捕获环境的更通用的代码场景中我们不希望有这些开销。因为函数从未允许捕获环境,定义和使用函数也就从不会有这些额外开销。")]),t._v(" "),t._m(98),t._v(" "),t._m(99),t._v(" "),t._m(100),t._v(" "),t._m(101),t._v(" "),t._m(102),t._v(" "),t._m(103),t._v(" "),t._m(104),n("p",[t._v("这个例子并不能编译:")]),t._v(" "),t._m(105),t._m(106),t._v(" "),t._m(107),t._v(" "),n("p",[t._v("为了展示闭包作为函数参数时捕获其环境的作用,让我们移动到下一个主题:迭代器。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"闭包:可以捕获环境的匿名函数"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#闭包:可以捕获环境的匿名函数","aria-hidden":"true"}},[this._v("#")]),this._v(" 闭包:可以捕获环境的匿名函数")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Rust 的 "),s("strong",[this._v("闭包")]),this._v("("),s("em",[this._v("closures")]),this._v(")是可以保存进变量或作为参数传递给其他函数的匿名函数。可以在一个地方创建闭包,然后在不同的上下文中执行闭包运算。不同于函数,闭包允许捕获调用者作用域中的值。我们将展示闭包的这些功能如何复用代码和自定义行为。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用闭包创建行为的抽象"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用闭包创建行为的抽象","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用闭包创建行为的抽象")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里将通过调用 "),s("code",[this._v("simulated_expensive_calculation")]),this._v(" 函数来模拟调用假象的算法,如示例 13-1 所示,它会打印出 "),s("code",[this._v("calculating slowly...")]),this._v(",等待两秒,并接着返回传递给它的数字:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("time"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("simulated_expensive_calculation")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"calculating slowly..."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("sleep")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from_secs")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n intensity\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-1:一个用来代替假想计算的函数,它大约会执行两秒钟")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接下来,"),s("code",[this._v("main")]),this._v(" 函数中将会包含本例的健身 app 中的重要部分。这代表当用户请求健身计划时 app 会调用的代码。因为与 app 前端的交互与闭包的使用并不相关,所以我们将硬编码代表程序输入的值并打印输出。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[this._v("一个来自用户的 intensity 数字,请求健身计划时指定,它代表用户喜好低强度还是高强度健身。")]),this._v(" "),s("li",[this._v("一个随机数,其会在健身计划中生成变化。")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("程序的输出将会是建议的锻炼计划。示例 13-2 展示了我们将要使用的 "),s("code",[this._v("main")]),this._v(" 函数:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" simulated_user_specified_value "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" simulated_random_number "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("7")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("generate_workout")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n simulated_user_specified_value"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n simulated_random_number\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("generate_workout")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" random_number"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-2:"),s("code",[this._v("main")]),this._v(" 函数包含了用于 "),s("code",[this._v("generate_workout")]),this._v(" 函数的模拟用户输入和模拟随机数输入")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("处于简单考虑这里硬编码了 "),n("code",[t._v("simulated_user_specified_value")]),t._v(" 变量的值为 10 和 "),n("code",[t._v("simulated_random_number")]),t._v(" 变量的值为 7;一个实际的程序会从 app 前端获取强度系数并使用 "),n("code",[t._v("rand")]),t._v(" crate 来生成随机数,正如第二章的猜猜看游戏所做的那样。"),n("code",[t._v("main")]),t._v(" 函数使用模拟的输入值调用 "),n("code",[t._v("generate_workout")]),t._v(" 函数:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在有了执行上下文,让我们编写算法。示例 13-3 中的 "),s("code",[this._v("generate_workout")]),this._v(" 函数包含本例中我们最关心的 app 业务逻辑。本例中余下的代码修改都将在这个函数中进行:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("time"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("simulated_expensive_calculation")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("num"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"calculating slowly..."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("sleep")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from_secs")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# num\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("generate_workout")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" random_number"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" intensity "),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("25")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Today, do {} pushups!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("simulated_expensive_calculation")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Next, do {} situps!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("simulated_expensive_calculation")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" random_number "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Take a break today! Remember to stay hydrated!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Today, run for {} minutes!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("simulated_expensive_calculation")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-3:程序的业务逻辑,它根据输入并调用 "),s("code",[this._v("simulated_expensive_calculation")]),this._v(" 函数来打印出健身计划")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 13-3 中的代码有多处慢计算函数的调用。第一个 "),n("code",[t._v("if")]),t._v(" 块调用了 "),n("code",[t._v("simulated_expensive_calculation")]),t._v(" 两次,外部 "),n("code",[t._v("else")]),t._v(" 中的 "),n("code",[t._v("if")]),t._v(" 完全没有调用它,第二个 "),n("code",[t._v("else")]),t._v(" 中的代码调用了它一次。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("generate_workout")]),this._v(" 函数的期望行为是首先检查用户需要低强度(由小于 25 的系数表示)锻炼还是高强度(25 或以上)锻炼。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("低强度锻炼计划会根据由 "),s("code",[this._v("simulated_expensive_calculation")]),this._v(" 函数所模拟的复杂算法建议一定数量的俯卧撑和仰卧起坐。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("现在这份代码能够应对我们的需求了,但数据科学部门的同学告知我们将来会对调用 "),s("code",[this._v("simulated_expensive_calculation")]),this._v(" 的方式做出一些改变。为了在要做这些改动的时候简化更新步骤,我们将重构代码来让它只调用 "),s("code",[this._v("simulated_expensive_calculation")]),this._v(" 一次。同时还希望去掉目前多余的连续两次函数调用,并不希望在计算过程中增加任何其他此函数的调用。也就是说,我们不希望在完全无需其结果的情况调用函数,不过仍然希望只调用函数一次。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用函数重构"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用函数重构","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用函数重构")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("有多种方法可以重构此程序。我们首先尝试的是将重复的 "),s("code",[this._v("simulated_expensive_calculation")]),this._v(" 函数调用提取到一个变量中,如示例 13-4 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("time"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("simulated_expensive_calculation")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("num"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"calculating slowly..."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("sleep")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from_secs")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# num\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("generate_workout")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" random_number"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" expensive_result "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("simulated_expensive_calculation")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" intensity "),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("25")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Today, do {} pushups!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n expensive_result\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Next, do {} situps!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n expensive_result\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" random_number "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Take a break today! Remember to stay hydrated!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Today, run for {} minutes!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n expensive_result\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-4:将 "),s("code",[this._v("simulated_expensive_calculation")]),this._v(" 调用提取到一个位置,并将结果储存在变量 "),s("code",[this._v("expensive_result")]),this._v(" 中")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个修改统一了 "),s("code",[this._v("simulated_expensive_calculation")]),this._v(" 调用并解决了第一个 "),s("code",[this._v("if")]),this._v(" 块中不必要的两次调用函数的问题。不幸的是,现在所有的情况下都需要调用函数并等待结果,包括那个完全不需要这一结果的内部 "),s("code",[this._v("if")]),this._v(" 块。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们希望能够在程序的一个位置指定某些代码,并只在程序的某处实际需要结果的时候 "),s("strong",[this._v("执行")]),this._v(" 这些代码。这正是闭包的用武之地!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"重构使用闭包储存代码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#重构使用闭包储存代码","aria-hidden":"true"}},[this._v("#")]),this._v(" 重构使用闭包储存代码")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("不同于总是在 "),s("code",[this._v("if")]),this._v(" 块之前调用 "),s("code",[this._v("simulated_expensive_calculation")]),this._v(" 函数并储存其结果,我们可以定义一个闭包并将其储存在变量中,如示例 13-5 所示。实际上可以选择将整个 "),s("code",[this._v("simulated_expensive_calculation")]),this._v(" 函数体移动到这里引入的闭包中:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("time"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" expensive_closure "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),t._v("num"),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"calculating slowly..."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("sleep")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from_secs")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n num\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("expensive_closure")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-5:定义一个闭包并储存到变量 "),s("code",[this._v("expensive_closure")]),this._v(" 中")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("闭包定义是 "),n("code",[t._v("expensive_closure")]),t._v(" 赋值的 "),n("code",[t._v("=")]),t._v(" 之后的部分。闭包的定义以一对竖线("),n("code",[t._v("|")]),t._v(")开始,在竖线中指定闭包的参数;之所以选择这个语法是因为它与 Smalltalk 和 Ruby 的闭包定义类似。这个闭包有一个参数 "),n("code",[t._v("num")]),t._v(";如果有多于一个参数,可以使用逗号分隔,比如 "),n("code",[t._v("|param1, param2|")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("参数之后是存放闭包体的大括号 —— 如果闭包体只有一行则大括号是可以省略的。大括号之后闭包的结尾,需要用于 "),s("code",[this._v("let")]),this._v(" 语句的分号。闭包体的最后一行("),s("code",[this._v("num")]),this._v(")返回的值将是调用闭包时返回的值,因为最后一行没有分号;正如函数体中的一样。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意这个 "),n("code",[t._v("let")]),t._v(" 语句意味着 "),n("code",[t._v("expensive_closure")]),t._v(" 包含一个匿名函数的 "),n("strong",[t._v("定义")]),t._v(",不是调用匿名函数的 "),n("strong",[t._v("返回值")]),t._v("。回忆一下使用闭包的原因是我们需要在一个位置定义代码,储存代码,并在之后的位置实际调用它;期望调用的代码现在储存在 "),n("code",[t._v("expensive_closure")]),t._v(" 中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("定义了闭包之后,可以改变 "),s("code",[this._v("if")]),this._v(" 块中的代码来调用闭包以执行代码并获取结果值。调用闭包类似于调用函数;指定存放闭包定义的变量名并后跟包含期望使用的参数的括号,如示例 13-6 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("time"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("generate_workout")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" random_number"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" expensive_closure "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),t._v("num"),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"calculating slowly..."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("sleep")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from_secs")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n num\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" intensity "),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("25")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Today, do {} pushups!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("expensive_closure")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Next, do {} situps!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("expensive_closure")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" random_number "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Take a break today! Remember to stay hydrated!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Today, run for {} minutes!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("expensive_closure")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-6:调用定义的 "),s("code",[this._v("expensive_closure")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("然而,我们又重新引入了示例 13-3 中的问题:仍然在第一个 "),s("code",[this._v("if")]),this._v(" 块中调用了闭包两次,这会调用慢计算两次并使用户多等待一倍的时间。可以通过在 "),s("code",[this._v("if")]),this._v(" 块中创建一个本地变量存放闭包调用的结果来解决这个问题,不过正因为使用了闭包还有另一个解决方案。稍后会回到这个方案上。首先讨论一下为何闭包定义中和所涉及的 trait 中没有类型注解。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"闭包类型推断和注解"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#闭包类型推断和注解","aria-hidden":"true"}},[this._v("#")]),this._v(" 闭包类型推断和注解")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("闭包不要求像 "),s("code",[this._v("fn")]),this._v(" 函数那样在参数和返回值上注明类型。函数中需要类型注解是因为他们是暴露给用户的显式接口的一部分。严格的定义这些接口对于保证所有人都认同函数使用和返回值的类型来说是很重要的。但是闭包并不用于这样暴露在外的接口:他们储存在变量中并被使用,不用命名他们或暴露给库的用户调用。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("time"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" expensive_closure "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),t._v("num"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"calculating slowly..."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("sleep")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from_secs")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n num\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-7:为闭包的参数和返回值增加可选的类型注解")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn add_one_v1 (x: u32) -> u32 { x + 1 }\nlet add_one_v2 = |x: u32| -> u32 { x + 1 };\nlet add_one_v3 = |x| { x + 1 };\nlet add_one_v4 = |x| x + 1 ;\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("闭包定义会为每个参数和返回值推断一个具体类型。例如,示例 13-8 中展示了仅仅将参数作为返回值的简短的闭包定义。除了作为示例的目的这个闭包并不是很实用。注意其定义并没有增加任何类型注解:如果尝试调用闭包两次,第一次使用 "),s("code",[this._v("String")]),this._v(" 类型作为参数而第二次使用 "),s("code",[this._v("u32")]),this._v(",则会得到一个错误:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('let example_closure = |x| x;\n\nlet s = example_closure(String::from("hello"));\nlet n = example_closure(5);\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-8:尝试调用一个被推断为两个不同类型的闭包")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0308]: mismatched types\n --\x3e src/main.rs\n |\n | let n = example_closure(5);\n | ^ expected struct `std::string::String`, found\n integral variable\n |\n = note: expected type `std::string::String`\n found type `{integer}`\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("第一次使用 "),n("code",[t._v("String")]),t._v(" 值调用 "),n("code",[t._v("example_closure")]),t._v(" 时,编译器推断 "),n("code",[t._v("x")]),t._v(" 和此闭包返回值的类型为 "),n("code",[t._v("String")]),t._v("。接着这些类型被锁定进闭包 "),n("code",[t._v("example_closure")]),t._v(" 中,如果尝试对同一闭包使用不同类型则会得到类型错误。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用带有泛型和-fn-trait-的闭包"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用带有泛型和-fn-trait-的闭包","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用带有泛型和 "),s("code",[this._v("Fn")]),this._v(" trait 的闭包")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("幸运的是,还有另一个可用的方案。可以创建一个存放闭包和调用闭包结果的结构体。该结构体只会在需要结果时执行闭包,并会缓存结果值,这样余下的代码就不必再负责保存结果并可以复用该值。你可能见过这种模式被称 "),s("em",[this._v("memoization")]),this._v(" 或 "),s("em",[this._v("lazy evaluation")]),this._v("。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Fn")]),t._v(" 系列 trait 由标准库提供。所有的闭包都实现了 trait "),n("code",[t._v("Fn")]),t._v("、"),n("code",[t._v("FnMut")]),t._v(" 或 "),n("code",[t._v("FnOnce")]),t._v(" 中的一个。在“闭包会捕获其环境”部分捕获环境部分我们会讨论这些 trait 的区别;在这个例子中可以使用 "),n("code",[t._v("Fn")]),t._v(" trait。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("为了满足 "),n("code",[t._v("Fn")]),t._v(" trait bound 我们增加了代表闭包所必须的参数和返回值类型的类型。在这个例子中,闭包有一个 "),n("code",[t._v("u32")]),t._v(" 的参数并返回一个 "),n("code",[t._v("u32")]),t._v(",这样所指定的 trait bound 就是 "),n("code",[t._v("Fn(u32) -> u32")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 13-9 展示了存放了闭包和一个 Option 结果值的 "),s("code",[this._v("Cacher")]),this._v(" 结构体的定义:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Cacher"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Fn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n calculation"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n value"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("u32"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-9:定义一个 "),s("code",[this._v("Cacher")]),this._v(" 结构体来在 "),s("code",[this._v("calculation")]),this._v(" 中存放闭包并在 "),s("code",[this._v("value")]),this._v(" 中存放 Option 值")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("结构体 "),n("code",[t._v("Cacher")]),t._v(" 有一个泛型 "),n("code",[t._v("T")]),t._v(" 的字段 "),n("code",[t._v("calculation")]),t._v("。"),n("code",[t._v("T")]),t._v(" 的 trait bound 指定了 "),n("code",[t._v("T")]),t._v(" 是一个使用 "),n("code",[t._v("Fn")]),t._v(" 的闭包。任何我们希望储存到 "),n("code",[t._v("Cacher")]),t._v(" 实例的 "),n("code",[t._v("calculation")]),t._v(" 字段的闭包必须有一个 "),n("code",[t._v("u32")]),t._v(" 参数(由 "),n("code",[t._v("Fn")]),t._v(" 之后的括号的内容指定)并必须返回一个 "),n("code",[t._v("u32")]),t._v("(由 "),n("code",[t._v("->")]),t._v(" 之后的内容)。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("blockquote",[s("p",[this._v("注意:函数也都实现了这三个 "),s("code",[this._v("Fn")]),this._v(" trait。如果不需要捕获环境中的值,则在需要实现 "),s("code",[this._v("Fn")]),this._v(" trait 是可以使用函数而不是闭包。")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("value")]),t._v(" 是 "),n("code",[t._v("Option")]),t._v(" 类型的。在执行闭包之前,"),n("code",[t._v("value")]),t._v(" 将是 "),n("code",[t._v("None")]),t._v("。如果使用 "),n("code",[t._v("Cacher")]),t._v(" 的代码请求闭包的结果,这时会执行闭包并将结果储存在 "),n("code",[t._v("value")]),t._v(" 字段的 "),n("code",[t._v("Some")]),t._v(" 成员中。接着如果代码再次请求闭包的结果,这时不再执行闭包,而是会返回存放在 "),n("code",[t._v("Some")]),t._v(" 成员中的结果。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("刚才讨论的有关 "),s("code",[this._v("value")]),this._v(" 字段逻辑定义于示例 13-10:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Cacher"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Fn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# calculation"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# value"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("u32"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" Cacher"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Fn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("calculation"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Cacher"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Cacher "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n calculation"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n value"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" None"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("value")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" v"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n None "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("calculation"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n v\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-10:"),s("code",[this._v("Cacher")]),this._v(" 的缓存逻辑")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("Cacher")]),this._v(" 结构体的字段是私有的,因为我们希望 "),s("code",[this._v("Cacher")]),this._v(" 管理这些值而不是任由调用代码潜在的直接改变他们。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Cacher::new")]),t._v(" 函数获取一个泛型参数 "),n("code",[t._v("T")]),t._v(",它定义于 "),n("code",[t._v("impl")]),t._v(" 块上下文中并与 "),n("code",[t._v("Cacher")]),t._v(" 结构体有着相同的 trait bound。"),n("code",[t._v("Cacher::new")]),t._v(" 返回一个在 "),n("code",[t._v("calculation")]),t._v(" 字段中存放了指定闭包和在 "),n("code",[t._v("value")]),t._v(" 字段中存放了 "),n("code",[t._v("None")]),t._v(" 值的 "),n("code",[t._v("Cacher")]),t._v(" 实例,因为我们还未执行闭包。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当调用代码需要闭包的执行结果时,不同于直接调用闭包,它会调用 "),n("code",[t._v("value")]),t._v(" 方法。这个方法会检查 "),n("code",[t._v("self.value")]),t._v(" 是否已经有了一个 "),n("code",[t._v("Some")]),t._v(" 的结果值;如果有,它返回 "),n("code",[t._v("Some")]),t._v(" 中的值并不会再次执行闭包。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("如果 "),n("code",[t._v("self.value")]),t._v(" 是 "),n("code",[t._v("None")]),t._v(",则会调用 "),n("code",[t._v("self.calculation")]),t._v(" 中储存的闭包,将结果保存到 "),n("code",[t._v("self.value")]),t._v(" 以便将来使用,并同时返回结果值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 13-11 展示了如何在示例 13-6 的 "),s("code",[this._v("generate_workout")]),this._v(" 函数中利用 "),s("code",[this._v("Cacher")]),this._v(" 结构体:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("time"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Cacher"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Fn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# calculation"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# value"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("u32"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" Cacher"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("where")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Fn")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("calculation"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" T"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Cacher"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("T"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# Cacher "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# calculation"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# value"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" None"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("value")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" u32 "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" v"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# None "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("calculation"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n# v\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("generate_workout")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" random_number"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" expensive_result "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Cacher"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token closure-params"}},[n("span",{attrs:{class:"token punctuation"}},[t._v("|")]),t._v("num"),n("span",{attrs:{class:"token punctuation"}},[t._v("|")])]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"calculating slowly..."')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n thread"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("sleep")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Duration"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from_secs")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n num\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" intensity "),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("25")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Today, do {} pushups!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n expensive_result"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("value")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Next, do {} situps!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n expensive_result"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("value")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" random_number "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Take a break today! Remember to stay hydrated!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{attrs:{class:"token string"}},[t._v('"Today, run for {} minutes!"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n expensive_result"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("value")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("intensity"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-11:在 "),s("code",[this._v("generate_workout")]),this._v(" 函数中利用 "),s("code",[this._v("Cacher")]),this._v(" 结构体来抽象出缓存逻辑")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("不同于直接将闭包保存进一个变量,我们保存一个新的 "),n("code",[t._v("Cacher")]),t._v(" 实例来存放闭包。接着,在每一个需要结果的地方,调用 "),n("code",[t._v("Cacher")]),t._v(" 实例的 "),n("code",[t._v("value")]),t._v(" 方法。可以调用 "),n("code",[t._v("value")]),t._v(" 方法任意多次,或者一次也不调用,而慢计算最多只会运行一次。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("尝试使用示例 13-2 中的 "),n("code",[t._v("main")]),t._v(" 函数来运行这段程序,并改变 "),n("code",[t._v("simulated_user_specified_value")]),t._v(" 和 "),n("code",[t._v("simulated_random_number")]),t._v(" 变量中的值来验证在所有情况下在多个 "),n("code",[t._v("if")]),t._v(" 和 "),n("code",[t._v("else")]),t._v(" 块中,闭包打印的 "),n("code",[t._v("calculating slowly...")]),t._v(" 只会在需要时出现并只会出现一次。"),n("code",[t._v("Cacher")]),t._v(" 负责确保不会调用超过所需的慢计算所需的逻辑,这样 "),n("code",[t._v("generate_workout")]),t._v(" 就可以专注业务逻辑了。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"cacher-实现的限制"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#cacher-实现的限制","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("Cacher")]),this._v(" 实现的限制")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("值缓存是一种更加广泛的实用行为,我们可能希望在代码中的其他闭包中也使用他们。然而,目前 "),s("code",[this._v("Cacher")]),this._v(" 的实现存在一些小问题,这使得在不同上下文中复用变得很困难。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("第一个问题是 "),n("code",[t._v("Cacher")]),t._v(" 实例假设对于 "),n("code",[t._v("value")]),t._v(" 方法的任何 "),n("code",[t._v("arg")]),t._v(" 参数值总是会返回相同的值。也就是说,这个 "),n("code",[t._v("Cacher")]),t._v(" 的测试会失败:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,panics extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("#[test]\nfn call_with_different_values() {\n let mut c = Cacher::new(|a| a);\n\n let v1 = c.value(1);\n let v2 = c.value(2);\n\n assert_eq!(v2, 2);\n}\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这个测试使用返回传递给它的值的闭包创建了一个新的 "),n("code",[t._v("Cacher")]),t._v(" 实例。使用为 1 的 "),n("code",[t._v("arg")]),t._v(" 和为 2 的 "),n("code",[t._v("arg")]),t._v(" 调用 "),n("code",[t._v("Cacher")]),t._v(" 实例的 "),n("code",[t._v("value")]),t._v(" 方法,同时我们期望使用为 2 的 "),n("code",[t._v("arg")]),t._v(" 调用 "),n("code",[t._v("value")]),t._v(" 会返回 2。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("使用示例 13-9 和示例 13-10 的 "),s("code",[this._v("Cacher")]),this._v(" 实现运行测试,它会在 "),s("code",[this._v("assert_eq!")]),this._v(" 失败并显示如下信息:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("thread 'call_with_different_values' panicked at 'assertion failed: `(left == right)`\n left: `1`,\n right: `2`', src/main.rs\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里的问题是第一次使用 1 调用 "),n("code",[t._v("c.value")]),t._v(","),n("code",[t._v("Cacher")]),t._v(" 实例将 "),n("code",[t._v("Some(1)")]),t._v(" 保存进 "),n("code",[t._v("self.value")]),t._v("。在这之后,无论传递什么值调用 "),n("code",[t._v("value")]),t._v(",它总是会返回 1。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("尝试修改 "),n("code",[t._v("Cacher")]),t._v(" 存放一个哈希 map 而不是单独一个值。哈希 map 的 key 将是传递进来的 "),n("code",[t._v("arg")]),t._v(" 值,而 value 则是对应 key 调用闭包的结果值。相比之前检查 "),n("code",[t._v("self.value")]),t._v(" 直接是 "),n("code",[t._v("Some")]),t._v(" 还是 "),n("code",[t._v("None")]),t._v(" 值,现在 "),n("code",[t._v("value")]),t._v(" 会在哈希 map 中寻找 "),n("code",[t._v("arg")]),t._v(",如果存在就返回它。如果不存在,"),n("code",[t._v("Cacher")]),t._v(" 会调用闭包并将结果值保存在哈希 map 对应 "),n("code",[t._v("arg")]),t._v(" 值的位置。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当前 "),n("code",[t._v("Cacher")]),t._v(" 实现的第二个问题是它的应用被限制为只接受获取一个 "),n("code",[t._v("u32")]),t._v(" 值并返回一个 "),n("code",[t._v("u32")]),t._v(" 值的闭包。比如说,我们可能需要能够缓存一个获取字符串 slice 并返回 "),n("code",[t._v("usize")]),t._v(" 值的闭包的结果。请尝试引入更多泛型参数来增加 "),n("code",[t._v("Cacher")]),t._v(" 功能的灵活性。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"闭包会捕获其环境"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#闭包会捕获其环境","aria-hidden":"true"}},[this._v("#")]),this._v(" 闭包会捕获其环境")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 13-12 有一个储存在 "),s("code",[this._v("equal_to_x")]),this._v(" 变量中闭包的例子,它使用了闭包环境中的变量 "),s("code",[this._v("x")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("4")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" equal_to_x "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v("z"),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v(" z "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" x"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" y "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("4")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("assert!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token function"}},[t._v("equal_to_x")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("y"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-12:一个引用了其周围作用域中变量的闭包示例")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这里,即便 "),n("code",[t._v("x")]),t._v(" 并不是 "),n("code",[t._v("equal_to_x")]),t._v(" 的一个参数,"),n("code",[t._v("equal_to_x")]),t._v(" 闭包也被允许使用变量 "),n("code",[t._v("x")]),t._v(",因为它与 "),n("code",[t._v("equal_to_x")]),t._v(" 定义于相同的作用域。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("fn main() {\n let x = 4;\n\n fn equal_to_x(z: i32) -> bool { z == x }\n\n let y = 4;\n\n assert!(equal_to_x(y));\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("error[E0434]: can't capture dynamic environment in a fn item; use the || { ...\n} closure form instead\n --\x3e src/main.rs\n |\n4 | fn equal_to_x(z: i32) -> bool { z == x }\n | ^\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("闭包可以通过三种方式捕获其环境,他们直接对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。这三种捕获值的方式被编码为如下三个 "),s("code",[this._v("Fn")]),this._v(" trait:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ul",[n("li",[n("code",[t._v("FnOnce")]),t._v(" 消费从周围作用域捕获的变量,闭包周围的作用域被称为其 "),n("strong",[t._v("环境")]),t._v(","),n("em",[t._v("environment")]),t._v("。为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。其名称的 "),n("code",[t._v("Once")]),t._v(" 部分代表了闭包不能多次获取相同变量的所有权的事实,所以它只能被调用一次。")]),t._v(" "),n("li",[n("code",[t._v("FnMut")]),t._v(" 获取可变的借用值所以可以改变其环境")]),t._v(" "),n("li",[n("code",[t._v("Fn")]),t._v(" 从其环境获取不可变的借用值")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("当创建一个闭包时,Rust 根据其如何使用环境中变量来推断我们希望如何引用环境。由于所有闭包都可以被调用至少一次,所以所有闭包都实现了 "),n("code",[t._v("FnOnce")]),t._v(" 。那些并没有移动被捕获变量的所有权到闭包内的闭包也实现了 "),n("code",[t._v("FnMut")]),t._v(" ,而不需要对被捕获的变量进行可变访问的闭包则也实现了 "),n("code",[t._v("Fn")]),t._v(" 。 在示例 13-12 中,"),n("code",[t._v("equal_to_x")]),t._v(" 闭包不可变的借用了 "),n("code",[t._v("x")]),t._v("(所以 "),n("code",[t._v("equal_to_x")]),t._v(" 具有 "),n("code",[t._v("Fn")]),t._v(" trait),因为闭包体只需要读取 "),n("code",[t._v("x")]),t._v(" 的值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("如果你希望强制闭包获取其使用的环境值的所有权,可以在参数列表前使用 "),s("code",[this._v("move")]),this._v(" 关键字。这个技巧在将闭包传递给新线程以便将数据移动到新线程中时最为实用。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("第十六章讨论并发时会展示更多 "),s("code",[this._v("move")]),this._v(" 闭包的例子,不过现在这里修改了示例 13-12 中的代码(作为演示),在闭包定义中增加 "),s("code",[this._v("move")]),this._v(" 关键字并使用 vector 代替整型,因为整型可以被拷贝而不是移动;注意这些代码还不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore,does_not_compile extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let x = vec![1, 2, 3];\n\n let equal_to_x = move |z| z == x;\n\n println!("can\'t use x here: {:?}", x);\n\n let y = vec![1, 2, 3];\n\n assert!(equal_to_x(y));\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('error[E0382]: use of moved value: `x`\n --\x3e src/main.rs:6:40\n |\n4 | let equal_to_x = move |z| z == x;\n | -------- value moved (into closure) here\n5 |\n6 | println!("can\'t use x here: {:?}", x);\n | ^ value used here after move\n |\n = note: move occurs because `x` has type `std::vec::Vec`, which does not\n implement the `Copy` trait\n')])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("x")]),t._v(" 被移动进了闭包,因为闭包使用 "),n("code",[t._v("move")]),t._v(" 关键字定义。接着闭包获取了 "),n("code",[t._v("x")]),t._v(" 的所有权,同时 "),n("code",[t._v("main")]),t._v(" 就不再允许在 "),n("code",[t._v("println!")]),t._v(" 语句中使用 "),n("code",[t._v("x")]),t._v(" 了。去掉 "),n("code",[t._v("println!")]),t._v(" 即可修复问题。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("大部分需要指定一个 "),n("code",[t._v("Fn")]),t._v(" trait bound 的时候,可以从 "),n("code",[t._v("Fn")]),t._v(" 开始,而编译器会根据闭包体中的情况告诉你是否需要 "),n("code",[t._v("FnMut")]),t._v(" 或 "),n("code",[t._v("FnOnce")]),t._v("。")])}],!1,null,null,null);e.options.__file="ch13-01-closures.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/68.d8ef845b.js b/src/.vuepress/dist/assets/js/68.d8ef845b.js
new file mode 100644
index 0000000..fdd299c
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/68.d8ef845b.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[68],{202:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch13-02-iterators.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch13-02-iterators.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._m(8),t._v(" "),n("p",[t._v("在标准库中没有提供迭代器的语言中,我们可能会使用一个从 0 开始的索引变量,使用这个变量索引 vector 中的值,并循环增加其值直到达到 vector 的元素数量。")]),t._v(" "),n("p",[t._v("迭代器为我们处理了所有这些逻辑,这减少了重复代码并消除了潜在的混乱。另外,迭代器的实现方式提供了对多种不同的序列使用相同逻辑的灵活性,而不仅仅是像 vector 这样可索引的数据结构.让我们看看迭代器是如何做到这些的。")]),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._m(32),t._v(" "),n("p",[t._v("得到的警告是:")]),t._v(" "),t._m(33),n("p",[t._v("示例 13-17 中的代码实际上并没有做任何事;所指定的闭包从未被调用过。警告提醒了我们为什么:迭代器适配器是惰性的,而这里我们需要消费迭代器。")]),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._v(" "),t._m(44),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._v(" "),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),t._m(56),t._v(" "),t._m(57),t._v(" "),t._m(58),t._v(" "),t._m(59),t._v(" "),t._m(60),t._m(61),t._v(" "),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._v(" "),t._m(65),t._v(" "),t._m(66),t._v(" "),t._m(67),t._m(68),t._v(" "),t._m(69),t._v(" "),t._m(70),t._v(" "),t._m(71),t._v(" "),t._m(72),t._v(" "),t._m(73),t._v(" "),t._m(74),t._m(75),t._v(" "),t._m(76),t._v(" "),t._m(77)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"使用迭代器处理元素序列"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用迭代器处理元素序列","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用迭代器处理元素序列")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("迭代器模式允许你对一个项的序列进行某些处理。"),s("strong",[this._v("迭代器")]),this._v("("),s("em",[this._v("iterator")]),this._v(")负责遍历序列中的每一项和决定序列何时结束的逻辑。当使用迭代器时,我们无需重新实现这些逻辑。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 Rust 中,迭代器是 "),n("strong",[t._v("惰性的")]),t._v("("),n("em",[t._v("lazy")]),t._v("),这意味着直到调用方法消费迭代器之前它都不会有效果。例如,示例 13-13 中的代码通过调用定义于 "),n("code",[t._v("Vec")]),t._v(" 上的 "),n("code",[t._v("iter")]),t._v(" 方法在一个 vector "),n("code",[t._v("v1")]),t._v(" 上创建了一个迭代器。这段代码本身没有任何用处:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v1_iter "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" v1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-13:创建一个迭代器")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("一旦创建迭代器之后,可以选择用多种方式利用它。在示例 3-4 中,我们使用迭代器和 "),s("code",[this._v("for")]),this._v(" 循环在每一个项上执行了一些代码,虽然直到现在为止我们一直掩盖了 "),s("code",[this._v("iter")]),this._v(" 调用做了什么。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("示例 13-14 中的例子将迭代器的创建和 "),n("code",[t._v("for")]),t._v(" 循环中的使用分开。迭代器被储存在 "),n("code",[t._v("v1_iter")]),t._v(" 变量中,而这时没有进行迭代。一旦 "),n("code",[t._v("for")]),t._v(" 循环开始使用 "),n("code",[t._v("v1_iter")]),t._v(",接着迭代器中的每一个元素被用于循环的一次迭代,这会打印出其每一个值:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v1_iter "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" v1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" val "),n("span",{attrs:{class:"token keyword"}},[t._v("in")]),t._v(" v1_iter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("println!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Got: {}"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" val"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-14:在一个 "),s("code",[this._v("for")]),this._v(" 循环中使用迭代器")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"iterator-trait-和-next-方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#iterator-trait-和-next-方法","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("Iterator")]),this._v(" trait 和 "),s("code",[this._v("next")]),this._v(" 方法")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("迭代器都实现了一个叫做 "),s("code",[this._v("Iterator")]),this._v(" 的定义于标准库的 trait。这个 trait 的定义看起来像这样:")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Iterator "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Item"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Self"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Item"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token comment"}},[t._v("// 此处省略了方法的默认实现")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意这里有一下我们还未讲到的新语法:"),n("code",[t._v("type Item")]),t._v(" 和 "),n("code",[t._v("Self::Item")]),t._v(",他们定义了 trait 的 "),n("strong",[t._v("关联类型")]),t._v("("),n("em",[t._v("associated type")]),t._v(")。第十九章会深入讲解关联类型,不过现在只需知道这段代码表明实现 "),n("code",[t._v("Iterator")]),t._v(" trait 要求同时定义一个 "),n("code",[t._v("Item")]),t._v(" 类型,这个 "),n("code",[t._v("Item")]),t._v(" 类型被用作 "),n("code",[t._v("next")]),t._v(" 方法的返回值类型。换句话说,"),n("code",[t._v("Item")]),t._v(" 类型将是迭代器返回元素的类型。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("next")]),t._v(" 是 "),n("code",[t._v("Iterator")]),t._v(" 实现者被要求定义的唯一方法。"),n("code",[t._v("next")]),t._v(" 一次返回迭代器中的一个项,封装在 "),n("code",[t._v("Some")]),t._v(" 中,当迭代器结束时,它返回 "),n("code",[t._v("None")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以直接调用迭代器的 "),s("code",[this._v("next")]),this._v(" 方法;示例 13-15 有一个测试展示了重复调用由 vector 创建的迭代器的 "),s("code",[this._v("next")]),this._v(" 方法所得到的值:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("iterator_demonstration")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" v1_iter "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" v1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v1_iter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v1_iter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v1_iter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v1_iter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" None"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-15:在迭代器上(直接)调用 "),s("code",[this._v("next")]),this._v(" 方法")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意 "),n("code",[t._v("v1_iter")]),t._v(" 需要是可变的:在迭代器上调用 "),n("code",[t._v("next")]),t._v(" 方法改变了迭代器中用来记录序列位置的状态。换句话说,代码 "),n("strong",[t._v("消费")]),t._v("(consume)了,或使用了迭代器。每一个 "),n("code",[t._v("next")]),t._v(" 调用都会从迭代器中消费一个项。使用 "),n("code",[t._v("for")]),t._v(" 循环时无需使 "),n("code",[t._v("v1_iter")]),t._v(" 可变因为 "),n("code",[t._v("for")]),t._v(" 循环会获取 "),n("code",[t._v("v1_iter")]),t._v(" 的所有权并在后台使 "),n("code",[t._v("v1_iter")]),t._v(" 可变。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("另外需要注意到从 "),n("code",[t._v("next")]),t._v(" 调用中得到的值是 vector 的不可变引用。"),n("code",[t._v("iter")]),t._v(" 方法生成一个不可变引用的迭代器。如果我们需要一个获取 "),n("code",[t._v("v1")]),t._v(" 所有权并返回拥有所有权的迭代器,则可以调用 "),n("code",[t._v("into_iter")]),t._v(" 而不是 "),n("code",[t._v("iter")]),t._v("。类似的,如果我们希望迭代可变引用,则可以调用 "),n("code",[t._v("iter_mut")]),t._v(" 而不是 "),n("code",[t._v("iter")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"消费迭代器的方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#消费迭代器的方法","aria-hidden":"true"}},[this._v("#")]),this._v(" 消费迭代器的方法")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Iterator")]),t._v(" trait 有一系列不同的由标准库提供默认实现的方法;你可以在 "),n("code",[t._v("Iterator")]),t._v(" trait 的标准库 API 文档中找到所有这些方法。一些方法在其定义中调用了 "),n("code",[t._v("next")]),t._v(" 方法,这也就是为什么在实现 "),n("code",[t._v("Iterator")]),t._v(" trait 时要求实现 "),n("code",[t._v("next")]),t._v(" 方法的原因。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("这些调用 "),n("code",[t._v("next")]),t._v(" 方法的方法被称为 "),n("strong",[t._v("消费适配器")]),t._v("("),n("em",[t._v("consuming adaptors")]),t._v("),因为调用他们会消耗迭代器。一个消费适配器的例子是 "),n("code",[t._v("sum")]),t._v(" 方法。这个方法获取迭代器的所有权并反复调用 "),n("code",[t._v("next")]),t._v(" 来遍历迭代器,因而会消费迭代器。当其遍历每一个项时,它将每一个项加总到一个总和并在迭代完成时返回总和。示例 13-16 有一个展示 "),n("code",[t._v("sum")]),t._v(" 方法使用的测试:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("iterator_sum")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v1 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v1_iter "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" v1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" total"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" v1_iter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("sum")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("total"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("6")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-16:调用 "),s("code",[this._v("sum")]),this._v(" 方法获取迭代器所有项的总和")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("调用 "),s("code",[this._v("sum")]),this._v(" 之后不再允许使用 "),s("code",[this._v("v1_iter")]),this._v(" 因为调用 "),s("code",[this._v("sum")]),this._v(" 时它会获取迭代器的所有权。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"产生其他迭代器的方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#产生其他迭代器的方法","aria-hidden":"true"}},[this._v("#")]),this._v(" 产生其他迭代器的方法")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("Iterator")]),this._v(" trait 中定义了另一类方法,被称为 "),s("strong",[this._v("迭代器适配器")]),this._v("("),s("em",[this._v("iterator adaptors")]),this._v("),他们允许我们将当前迭代器变为不同类型的迭代器。可以链式调用多个迭代器适配器。不过因为所有的迭代器都是惰性的,必须调用一个消费适配器方法以便获取迭代器适配器调用的结果。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 13-17 展示了一个调用迭代器适配器方法 "),s("code",[this._v("map")]),this._v(" 的例子,该 "),s("code",[this._v("map")]),this._v(" 方法使用闭包来调用每个元素以生成新的迭代器。 这里的闭包创建了一个新的迭代器,对其中 vector 中的每个元素都被加 1。不过这些代码会产生一个警告:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,not_desired_behavior extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("let v1: Vec = vec![1, 2, 3];\n\nv1.iter().map(|x| x + 1);\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-17:调用迭代器适配器 "),s("code",[this._v("map")]),this._v(" 来创建一个新迭代器")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-text extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("warning: unused `std::iter::Map` which must be used: iterator adaptors are lazy\nand do nothing unless consumed\n --\x3e src/main.rs:4:5\n |\n4 | v1.iter().map(|x| x + 1);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = note: #[warn(unused_must_use)] on by default\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("为了修复这个警告并消费迭代器获取有用的结果,我们将使用第十二章简要讲到的 "),s("code",[this._v("collect")]),this._v(" 方法。这个方法消费迭代器并将结果收集到一个数据结构中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在示例 13-18 中,我们将遍历由 "),s("code",[this._v("map")]),this._v(" 调用生成的迭代器的结果收集到一个 vector 中,它将会含有原始 vector 中每个元素加 1 的结果:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v1"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("i32"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" v2"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("_"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" v1"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("map")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v("x"),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("collect")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v2"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("4")]),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-18:调用 "),s("code",[this._v("map")]),this._v(" 方法创建一个新迭代器,接着调用 "),s("code",[this._v("collect")]),this._v(" 方法消费新迭代器并创建一个 vector")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("因为 "),s("code",[this._v("map")]),this._v(" 获取一个闭包,可以指定任何希望在遍历的每个元素上执行的操作。这是一个展示如何使用闭包来自定义行为同时又复用 "),s("code",[this._v("Iterator")]),this._v(" trait 提供的迭代行为的绝佳例子。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用闭包获取环境"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用闭包获取环境","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用闭包获取环境")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("现在我们介绍了迭代器,让我们展示一个通过使用 "),n("code",[t._v("filter")]),t._v(" 迭代器适配器和捕获环境的闭包的常规用例。迭代器的 "),n("code",[t._v("filter")]),t._v(" 方法获取一个使用迭代器的每一个项并返回布尔值的闭包。如果闭包返回 "),n("code",[t._v("true")]),t._v(",其值将会包含在 "),n("code",[t._v("filter")]),t._v(" 提供的新迭代器中。如果闭包返回 "),n("code",[t._v("false")]),t._v(",其值不会包含在结果迭代器中。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 13-19 展示了使用 "),s("code",[this._v("filter")]),this._v(" 和一个捕获环境中变量 "),s("code",[this._v("shoe_size")]),this._v(" 的闭包,这样闭包就可以遍历一个 "),s("code",[this._v("Shoe")]),this._v(" 结构体集合以便只返回指定大小的鞋子:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[derive(PartialEq, Debug)]")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Shoe "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n style"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("shoes_in_my_size")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("shoes"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Shoe"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" shoe_size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Vec"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Shoe"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n shoes"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("into_iter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("filter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v("s"),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v(" s"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("size "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" shoe_size"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("collect")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("filters_by_size")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" shoes "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n Shoe "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" style"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"sneaker"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Shoe "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("13")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" style"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"sandal"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Shoe "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" style"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"boot"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" in_my_size "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("shoes_in_my_size")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("shoes"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n in_my_size"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("vec!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n Shoe "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" style"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"sneaker"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Shoe "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" size"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("10")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" style"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("from")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"boot"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-19:使用 "),s("code",[this._v("filter")]),this._v(" 方法和一个捕获 "),s("code",[this._v("shoe_size")]),this._v(" 的闭包")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("shoes_in_my_size")]),this._v(" 函数获取一个鞋子 vector 的所有权和一个鞋子大小作为参数。它返回一个只包含指定大小鞋子的 vector。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在 "),n("code",[t._v("shoes_in_my_size")]),t._v(" 函数体中调用了 "),n("code",[t._v("into_iter")]),t._v(" 来创建一个获取 vector 所有权的迭代器。接着调用 "),n("code",[t._v("filter")]),t._v(" 将这个迭代器适配成只含有闭包返回 "),n("code",[t._v("true")]),t._v(" 元素的新迭代器。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("闭包从环境中捕获了 "),s("code",[this._v("shoe_size")]),this._v(" 变量并使用其值与每一只鞋的大小作比较,只保留指定大小的鞋子。最终,调用 "),s("code",[this._v("collect")]),this._v(" 将迭代器适配器返回的值收集进一个 vector 并返回。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个测试展示当调用 "),s("code",[this._v("shoes_in_my_size")]),this._v(" 时,我们只会得到与指定值相同大小的鞋子。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"实现-iterator-trait-来创建自定义迭代器"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#实现-iterator-trait-来创建自定义迭代器","aria-hidden":"true"}},[this._v("#")]),this._v(" 实现 "),s("code",[this._v("Iterator")]),this._v(" trait 来创建自定义迭代器")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们已经展示了可以通过在 vector 上调用 "),n("code",[t._v("iter")]),t._v("、"),n("code",[t._v("into_iter")]),t._v(" 或 "),n("code",[t._v("iter_mut")]),t._v(" 来创建一个迭代器。也可以用标准库中其他的集合类型创建迭代器,比如哈希 map。另外,可以实现 "),n("code",[t._v("Iterator")]),t._v(" trait 来创建任何我们希望的迭代器。正如之前提到的,定义中唯一要求提供的方法就是 "),n("code",[t._v("next")]),t._v(" 方法。一旦定义了它,就可以使用所有其他由 "),n("code",[t._v("Iterator")]),t._v(" trait 提供的拥有默认实现的方法来创建自定义迭代器了!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("作为展示,让我们创建一个只会从 1 数到 5 的迭代器。首先,创建一个结构体来存放一些值,接着实现 "),s("code",[this._v("Iterator")]),this._v(" trait 将这个结构体放入迭代器中并在此实现中使用其值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("示例 13-20 有一个 "),s("code",[this._v("Counter")]),this._v(" 结构体定义和一个创建 "),s("code",[this._v("Counter")]),this._v(" 实例的关联函数 "),s("code",[this._v("new")]),this._v(":")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("span",{staticClass:"caption"},[t._v("示例 13-20:定义 "),n("code",[t._v("Counter")]),t._v(" 结构体和一个创建 "),n("code",[t._v("count")]),t._v(" 初值为 0 的 "),n("code",[t._v("Counter")]),t._v(" 实例的 "),n("code",[t._v("new")]),t._v(" 函数")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("Counter")]),t._v(" 结构体有一个字段 "),n("code",[t._v("count")]),t._v("。这个字段存放一个 "),n("code",[t._v("u32")]),t._v(" 值,它会记录处理 1 到 5 的迭代过程中的位置。"),n("code",[t._v("count")]),t._v(" 是私有的因为我们希望 "),n("code",[t._v("Counter")]),t._v(" 的实现来管理这个值。"),n("code",[t._v("new")]),t._v(" 函数通过总是从为 0 的 "),n("code",[t._v("count")]),t._v(" 字段开始新实例来确保我们需要的行为。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接下来将为 "),s("code",[this._v("Counter")]),this._v(" 类型实现 "),s("code",[this._v("Iterator")]),this._v(" trait,通过定义 "),s("code",[this._v("next")]),this._v(" 方法来指定使用迭代器时的行为,如示例 13-21 所示:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Iterator "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Item "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Self"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Item"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count "),n("span",{attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count "),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("6")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n None\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-21:在 "),s("code",[this._v("Counter")]),this._v(" 结构体上实现 "),s("code",[this._v("Iterator")]),this._v(" trait")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这里将迭代器的关联类型 "),s("code",[this._v("Item")]),this._v(" 设置为 "),s("code",[this._v("u32")]),this._v(",意味着迭代器会返回 "),s("code",[this._v("u32")]),this._v(" 值集合。再一次,这里仍无需担心关联类型,第十九章会讲到。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("我们希望迭代器对其内部状态加一,这也就是为何将 "),n("code",[t._v("count")]),t._v(" 初始化为 0:我们希望迭代器首先返回 1。如果 "),n("code",[t._v("count")]),t._v(" 值小于 6,"),n("code",[t._v("next")]),t._v(" 会返回封装在 "),n("code",[t._v("Some")]),t._v(" 中的当前值,不过如果 "),n("code",[t._v("count")]),t._v(" 大于或等于 6,迭代器会返回 "),n("code",[t._v("None")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用-counter-迭代器的-next-方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-counter-迭代器的-next-方法","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("Counter")]),this._v(" 迭代器的 "),s("code",[this._v("next")]),this._v(" 方法")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("一旦实现了 "),s("code",[this._v("Iterator")]),this._v(" trait,我们就有了一个迭代器!示例 13-22 展示了一个测试用来演示使用 "),s("code",[this._v("Counter")]),this._v(" 结构体的迭代器功能,通过直接调用 "),s("code",[this._v("next")]),this._v(" 方法,正如示例 13-15 中从 vector 创建的迭代器那样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Iterator "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Item "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Self"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Item"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count "),n("span",{attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count "),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("6")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# None\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("calling_next_directly")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" counter "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Counter"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("counter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("counter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("2")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("counter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("3")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("counter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("4")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("counter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("5")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("counter"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" None"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-22:测试 "),s("code",[this._v("next")]),this._v(" 方法实现的功能")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这个测试在 "),s("code",[this._v("counter")]),this._v(" 变量中新建了一个 "),s("code",[this._v("Counter")]),this._v(" 实例并接着反复调用 "),s("code",[this._v("next")]),this._v(" 方法,来验证我们实现的行为符合这个迭代器返回从 1 到 5 的值的预期。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用自定义迭代器中其他-iterator-trait-方法"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用自定义迭代器中其他-iterator-trait-方法","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用自定义迭代器中其他 "),s("code",[this._v("Iterator")]),this._v(" trait 方法")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("通过定义 "),n("code",[t._v("next")]),t._v(" 方法实现 "),n("code",[t._v("Iterator")]),t._v(" trait,我们现在就可以使用任何标准库定义的拥有默认实现的 "),n("code",[t._v("Iterator")]),t._v(" trait 方法了,因为他们都使用了 "),n("code",[t._v("next")]),t._v(" 方法的功能。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("例如,出于某种原因我们希望获取 "),s("code",[this._v("Counter")]),this._v(" 实例产生的值,将这些值与另一个 "),s("code",[this._v("Counter")]),this._v(" 实例在省略了第一个值之后产生的值配对,将每一对值相乘,只保留那些可以被三整除的结果,然后将所有保留的结果相加,这可以如示例 13-23 中的测试这样做:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" count"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Iterator "),n("span",{attrs:{class:"token keyword"}},[t._v("for")]),t._v(" Counter "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token comment"}},[t._v("// Our iterator will produce u32s")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Item "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" u32"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Option"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Self"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Item"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token comment"}},[t._v("// increment our count. This is why we started at zero.")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count "),n("span",{attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),n("span",{attrs:{class:"token comment"}},[t._v("// check to see if we've finished counting or not.")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count "),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("6")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("self")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),t._v("count"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# None\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n"),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("using_other_iterator_trait_methods")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" sum"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" u32 "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" Counter"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("zip")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Counter"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("skip")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("1")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("map")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("|")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v(" a "),n("span",{attrs:{class:"token operator"}},[t._v("*")]),t._v(" b"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("filter")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v("x"),n("span",{attrs:{class:"token operator"}},[t._v("|")]),t._v(" x "),n("span",{attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("3")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{attrs:{class:"token number"}},[t._v("0")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("sum")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token number"}},[t._v("18")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" sum"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-23:使用自定义的 "),s("code",[this._v("Counter")]),this._v(" 迭代器的多种方法")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("注意 "),n("code",[t._v("zip")]),t._v(" 只产生四对值;理论上第五对值 "),n("code",[t._v("(5, None)")]),t._v(" 从未被产生,因为 "),n("code",[t._v("zip")]),t._v(" 在任一输入迭代器返回 "),n("code",[t._v("None")]),t._v(" 时也返回 "),n("code",[t._v("None")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("所有这些方法调用都是可能的,因为我们指定了 "),s("code",[this._v("next")]),this._v(" 方法如何工作,而标准库则提供了其它调用 "),s("code",[this._v("next")]),this._v(" 的方法的默认实现。")])}],!1,null,null,null);e.options.__file="ch13-02-iterators.md";s.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/69.5b80f65b.js b/src/.vuepress/dist/assets/js/69.5b80f65b.js
new file mode 100644
index 0000000..c8cbde8
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/69.5b80f65b.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[69],{201:function(t,s,n){"use strict";n.r(s);var e=n(0),a=Object(e.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch13-03-improving-our-io-project.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch13-03-improving-our-io-project.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._v(" "),n("p",[t._v("接下来的逻辑问题就是在代码中应该选择哪种风格:示例 13-28 中的原始实现,或者是示例 13-29 中使用迭代器的版本。大部分 Rust 程序员倾向于使用迭代器风格。开始这有点难以理解,不过一旦你对不同迭代器的工作方式有了感觉之后,迭代器可能会更容易理解。相比摆弄不同的循环并创建新 vector,(迭代器)代码则更关注循环的目的。这抽象出了那些老生常谈的代码,这样就更容易看清代码所特有的概念,比如迭代器中每个元素必须面对的过滤条件。")]),t._v(" "),n("p",[t._v("不过这两种实现真的完全等同吗?直觉上的假设是更底层的循环会更快一些。让我们聊聊性能吧。")])])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"改进-i-o-项目"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#改进-i-o-项目","aria-hidden":"true"}},[this._v("#")]),this._v(" 改进 I/O 项目")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("有了这些关于迭代器的新知识,我们可以使用迭代器来改进第十二章中 I/O 项目的实现来使得代码更简洁明了。让我们看看迭代器如何能够改进 "),s("code",[this._v("Config::new")]),this._v(" 函数和 "),s("code",[this._v("search")]),this._v(" 函数的实现。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用迭代器并去掉-clone"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用迭代器并去掉-clone","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用迭代器并去掉 "),s("code",[this._v("clone")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("在示例 12-6 中,我们增加了一些代码获取一个 "),n("code",[t._v("String")]),t._v(" slice 并创建一个 "),n("code",[t._v("Config")]),t._v(" 结构体的实例,他们索引 slice 中的值并克隆这些值以便 "),n("code",[t._v("Config")]),t._v(" 结构体可以拥有这些值。在示例 13-24 中重现了第十二章结尾示例 12-23 中 "),n("code",[t._v("Config::new")]),t._v(" 函数的实现:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('impl Config {\n pub fn new(args: &[String]) -> Result {\n if args.len() < 3 {\n return Err("not enough arguments");\n }\n\n let query = args[1].clone();\n let filename = args[2].clone();\n\n let case_sensitive = env::var("CASE_INSENSITIVE").is_err();\n\n Ok(Config { query, filename, case_sensitive })\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-24:重现第十二章结尾的 "),s("code",[this._v("Config::new")]),this._v(" 函数")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("这时可以不必担心低效的 "),s("code",[this._v("clone")]),this._v(" 调用了,因为将来可以去掉他们。好吧,就是现在!")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("起初这里需要 "),n("code",[t._v("clone")]),t._v(" 的原因是参数 "),n("code",[t._v("args")]),t._v(" 中有一个 "),n("code",[t._v("String")]),t._v(" 元素的 slice,而 "),n("code",[t._v("new")]),t._v(" 函数并不拥有 "),n("code",[t._v("args")]),t._v("。为了能够返回 "),n("code",[t._v("Config")]),t._v(" 实例的所有权,我们需要克隆 "),n("code",[t._v("Config")]),t._v(" 中字段 "),n("code",[t._v("query")]),t._v(" 和 "),n("code",[t._v("filename")]),t._v(" 的值,这样 "),n("code",[t._v("Config")]),t._v(" 实例就能拥有这些值。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("通过迭代器的新知识,我们可以将 "),s("code",[this._v("new")]),this._v(" 函数改为获取一个有所有权的迭代器作为参数而不是借用 slice。我们将使用迭代器功能之前检查 slice 长度和索引特定位置的代码。这会明确 "),s("code",[this._v("Config::new")]),this._v(" 的工作因为迭代器会负责访问这些值。")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("一旦 "),n("code",[t._v("Config::new")]),t._v(" 获取了迭代器的所有权并不再使用借用的索引操作,就可以将迭代器中的 "),n("code",[t._v("String")]),t._v(" 值移动到 "),n("code",[t._v("Config")]),t._v(" 中,而不是调用 "),n("code",[t._v("clone")]),t._v(" 分配新的空间。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"直接使用-env-args-返回的迭代器"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#直接使用-env-args-返回的迭代器","aria-hidden":"true"}},[this._v("#")]),this._v(" 直接使用 "),s("code",[this._v("env::args")]),this._v(" 返回的迭代器")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("打开 I/O 项目的 "),s("em",[this._v("src/main.rs")]),this._v(" 文件,它看起来应该像这样:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let args: Vec = env::args().collect();\n\n let config = Config::new(&args).unwrap_or_else(|err| {\n eprintln!("Problem parsing arguments: {}", err);\n process::exit(1);\n });\n\n // --snip--\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们会修改第十二章结尾示例 12-24 中的 "),s("code",[this._v("main")]),this._v(" 函数的开头为示例 13-25 中的代码。直到同步更新 "),s("code",[this._v("Config::new")]),this._v(" 之前这些代码还不能编译:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('fn main() {\n let config = Config::new(env::args()).unwrap_or_else(|err| {\n eprintln!("Problem parsing arguments: {}", err);\n process::exit(1);\n });\n\n // --snip--\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-25:将 "),s("code",[this._v("env::args")]),this._v(" 的返回值传递给 "),s("code",[this._v("Config::new")])])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("env::args")]),t._v(" 函数返回一个迭代器!不同于将迭代器的值收集到一个 vector 中接着传递一个 slice 给 "),n("code",[t._v("Config::new")]),t._v(",现在我们直接将 "),n("code",[t._v("env::args")]),t._v(" 返回的迭代器的所有权传递给 "),n("code",[t._v("Config::new")]),t._v("。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("接下来需要更新 "),s("code",[this._v("Config::new")]),this._v(" 的定义。在 I/O 项目的 "),s("em",[this._v("src/lib.rs")]),this._v(" 中,将 "),s("code",[this._v("Config::new")]),this._v(" 的签名改为如示例 13-26 所示。这仍然不能编译因为我们还需更新函数体:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("impl Config {\n pub fn new(mut args: std::env::Args) -> Result {\n // --snip--\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-26:更新 "),s("code",[this._v("Config::new")]),this._v(" 的签名来接受一个迭代器")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[n("code",[t._v("env::args")]),t._v(" 函数的标准库文档展示了其返回的迭代器类型是 "),n("code",[t._v("std::env::Args")]),t._v("。需要更新 "),n("code",[t._v("Config::new")]),t._v(" 函数的签名中 "),n("code",[t._v("args")]),t._v(" 参数的类型为 "),n("code",[t._v("std::env::Args")]),t._v(" 而不是 "),n("code",[t._v("&[String]")]),t._v("。因为这里需要获取 "),n("code",[t._v("args")]),t._v(" 的所有权且通过迭代改变 "),n("code",[t._v("args")]),t._v(",我们可以在 "),n("code",[t._v("args")]),t._v(" 参数前指定 "),n("code",[t._v("mut")]),t._v(" 关键字使其可变。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h4",{attrs:{id:"使用-iterator-trait-方法代替索引"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-iterator-trait-方法代替索引","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("Iterator")]),this._v(" trait 方法代替索引")])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("接下来修复 "),n("code",[t._v("Config::new")]),t._v(" 的函数体。标准库文档也提到了 "),n("code",[t._v("std::env::Args")]),t._v(" 实现了 "),n("code",[t._v("Iterator")]),t._v(" trait,所以可以在其上调用 "),n("code",[t._v("next")]),t._v(" 方法!示例 13-27 更新了示例 12-23 中的代码为使用 "),n("code",[t._v("next")]),t._v(" 方法:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"language-rust extra-class"},[n("pre",{pre:!0,attrs:{class:"language-rust"}},[n("code",[t._v("# "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("main")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("env"),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n#\n# "),n("span",{attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" Config "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n# query"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" String"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# case_sensitive"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bool"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n# "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n#\n"),n("span",{attrs:{class:"token keyword"}},[t._v("impl")]),t._v(" Config "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("new")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token keyword"}},[t._v("mut")]),t._v(" args"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" std"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("env"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v("Args"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" Result"),n("span",{attrs:{class:"token operator"}},[t._v("<")]),t._v("Config"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("&")]),n("span",{attrs:{class:"token lifetime-annotation symbol"}},[t._v("'static")]),t._v(" str"),n("span",{attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n args"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" query "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" args"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n None "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Didn\'t get a query string"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" filename "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("match")]),t._v(" args"),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("next")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{attrs:{class:"token function"}},[t._v("Some")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" arg"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n None "),n("span",{attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),n("span",{attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{attrs:{class:"token function"}},[t._v("Err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"Didn\'t get a file name"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token keyword"}},[t._v("let")]),t._v(" case_sensitive "),n("span",{attrs:{class:"token operator"}},[t._v("=")]),t._v(" env"),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{attrs:{class:"token function"}},[t._v("var")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token string"}},[t._v('"CASE_INSENSITIVE"')]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{attrs:{class:"token function"}},[t._v("is_err")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),n("span",{attrs:{class:"token function"}},[t._v("Ok")]),n("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Config "),n("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" query"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" filename"),n("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" case_sensitive "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-27:修改 "),s("code",[this._v("Config::new")]),this._v(" 的函数体来使用迭代器方法")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("请记住 "),n("code",[t._v("env::args")]),t._v(" 返回值的第一个值是程序的名称。我们希望忽略它并获取下一个值,所以首先调用 "),n("code",[t._v("next")]),t._v(" 并不对返回值做任何操作。之后对希望放入 "),n("code",[t._v("Config")]),t._v(" 中字段 "),n("code",[t._v("query")]),t._v(" 调用 "),n("code",[t._v("next")]),t._v("。如果 "),n("code",[t._v("next")]),t._v(" 返回 "),n("code",[t._v("Some")]),t._v(",使用 "),n("code",[t._v("match")]),t._v(" 来提取其值。如果它返回 "),n("code",[t._v("None")]),t._v(",则意味着没有提供足够的参数并通过 "),n("code",[t._v("Err")]),t._v(" 值提早返回。对 "),n("code",[t._v("filename")]),t._v(" 值进行同样的操作。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用迭代器适配器来使代码更简明"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用迭代器适配器来使代码更简明","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用迭代器适配器来使代码更简明")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("I/O 项目中其他可以利用迭代器优势的地方位于 "),s("code",[this._v("search")]),this._v(" 函数,示例 13-28 中重现了第十二章结尾示例 12-19 中此函数的定义:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {\n let mut results = Vec::new();\n\n for line in contents.lines() {\n if line.contains(query) {\n results.push(line);\n }\n }\n\n results\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-28:示例 12-19 中 "),s("code",[this._v("search")]),this._v(" 函数的定义")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("可以通过使用迭代器适配器方法来编写更简明的代码。这也避免了一个可变的中间 "),s("code",[this._v("results")]),this._v(" vector 的使用。函数式编程风格倾向于最小化可变状态的数量来使代码更简洁。去掉可变状态可能会使得将来进行并行搜索的增强变得更容易,因为我们不必管理 "),s("code",[this._v("results")]),this._v(" vector 的并发访问。示例 13-29 展示了该变化:")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-rust,ignore extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {\n contents.lines()\n .filter(|line| line.contains(query))\n .collect()\n}\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("span",{staticClass:"caption"},[this._v("示例 13-29:在 "),s("code",[this._v("search")]),this._v(" 函数实现中使用迭代器适配器")])])},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("p",[t._v("回忆 "),n("code",[t._v("search")]),t._v(" 函数的目的是返回所有 "),n("code",[t._v("contents")]),t._v(" 中包含 "),n("code",[t._v("query")]),t._v(" 的行。类似于示例 13-19 中的 "),n("code",[t._v("filter")]),t._v(" 例子,可以使用 "),n("code",[t._v("filter")]),t._v(" 适配器只保留 "),n("code",[t._v("line.contains(query)")]),t._v(" 返回 "),n("code",[t._v("true")]),t._v(" 的那些行。接着使用 "),n("code",[t._v("collect")]),t._v(" 将匹配行收集到另一个 vector 中。这样就容易多了!请随意对 "),n("code",[t._v("search_case_insensitive")]),t._v(" 函数做出同样的使用迭代器方法的修改。")])}],!1,null,null,null);a.options.__file="ch13-03-improving-our-io-project.md";s.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/7.010146f6.js b/src/.vuepress/dist/assets/js/7.010146f6.js
new file mode 100644
index 0000000..74ceeb8
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/7.010146f6.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{260:function(_,t,v){"use strict";v.r(t);var d=v(0),e=Object(d.a)({},function(){var _=this,t=_.$createElement,v=_._self._c||t;return v("div",{staticClass:"content"},[_._m(0),_._v(" "),v("blockquote",[v("p",[v("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/appendix-02-operators.md",target:"_blank",rel:"noopener noreferrer"}},[_._v("appendix-02-operators.md"),v("OutboundLink")],1),_._v(" "),v("br"),_._v("\ncommit d50521fc08e51892cdf1edf5e35f3847a42f9432")])]),_._v(" "),v("p",[_._v("该附录包含了 Rust 语法的词汇表,包括运算符以及其他的符号,这些符号以其自身或者在路径、泛型、trait bounds、宏、属性、注释、元组以及大括号的上下文中出现。")]),_._v(" "),_._m(1),_._v(" "),v("p",[_._v("表B-1包含了 Rust 中的运算符、运算符如何出现在上下文中的示例、简短解释以及该运算符是否可重载。如果一个运算符是可重载的,则该运算符上用于重载的相关 trait 也会列出。")]),_._v(" "),_._m(2),_._v(" "),_._m(3),_._v(" "),_._m(4),_._v(" "),v("p",[_._v("下面的列表中包含了所有和运算符不一样功能的非字符符号;也就是说,他们并不像函数调用或方法调用一样表现。")]),_._v(" "),v("p",[_._v("表 B-2 展示了以其自身出现以及出现在合法其他各个地方的符号。")]),_._v(" "),_._m(5),_._v(" "),_._m(6),_._v(" "),_._m(7),_._v(" "),_._m(8),_._v(" "),v("p",[_._v("表 B-4 展示了出现在泛型类型参数上下文中的符号。")]),_._v(" "),_._m(9),_._v(" "),v("table",[_._m(10),_._v(" "),v("tbody",[_._m(11),_._v(" "),v("tr",[_._m(12),_._v(" "),v("td",[_._v("为一个泛型、函数或表达式中的方法指定具体参数,通常指 "),v("a",{attrs:{href:"https://matematikaadit.github.io/posts/rust-turbofish.html",target:"_blank",rel:"noopener noreferrer"}},[_._v("turbofish"),v("OutboundLink")],1),_._v(" (如 "),v("code",[_._v('"42".parse::()')]),_._v(")")])]),_._v(" "),_._m(13),_._v(" "),_._m(14),_._v(" "),_._m(15),_._v(" "),_._m(16),_._v(" "),_._m(17),_._v(" "),_._m(18)])]),_._v(" "),v("p",[_._v("表 B-5 展示了出现在使用 trait bounds 约束泛型参数上下文中的符号。")]),_._v(" "),_._m(19),_._v(" "),_._m(20),_._v(" "),v("p",[_._v("表 B-6 展示了在调用或定义宏以及在其上指定属性时的上下文中出现的符号。")]),_._v(" "),_._m(21),_._v(" "),_._m(22),_._v(" "),v("p",[_._v("表 B-7 展示了写注释的符号。")]),_._v(" "),_._m(23),_._v(" "),_._m(24),_._v(" "),v("p",[_._v("表 B-8 展示了出现在使用元组时上下文中的符号。")]),_._v(" "),_._m(25),_._v(" "),v("p",[_._v("表 B-9 使用大括号的符号。")]),_._v(" "),_._m(26),_._v(" "),v("p",[_._v("表 B-10 展示了使用方括号的符号。")]),_._v(" "),_._m(27),_._v(" "),_._m(28)])},[function(){var _=this.$createElement,t=this._self._c||_;return t("h2",{attrs:{id:"附录b-运算符与符号"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#附录b-运算符与符号","aria-hidden":"true"}},[this._v("#")]),this._v(" 附录B - 运算符与符号")])},function(){var _=this.$createElement,t=this._self._c||_;return t("h3",{attrs:{id:"运算符"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#运算符","aria-hidden":"true"}},[this._v("#")]),this._v(" 运算符")])},function(){var _=this.$createElement,t=this._self._c||_;return t("p",[t("span",{staticClass:"caption"},[this._v("表 B-1: 运算符")])])},function(){var _=this,t=_.$createElement,v=_._self._c||t;return v("table",[v("thead",[v("tr",[v("th",[_._v("运算符")]),_._v(" "),v("th",[_._v("示例")]),_._v(" "),v("th",[_._v("解释")]),_._v(" "),v("th",[_._v("是否可重载")])])]),_._v(" "),v("tbody",[v("tr",[v("td",[v("code",[_._v("!")])]),_._v(" "),v("td",[v("code",[_._v("ident!(...)")]),_._v(", "),v("code",[_._v("ident!{...}")]),_._v(", "),v("code",[_._v("ident![...]")])]),_._v(" "),v("td",[_._v("宏扩展")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("!")])]),_._v(" "),v("td",[v("code",[_._v("!expr")])]),_._v(" "),v("td",[_._v("按位非或逻辑非")]),_._v(" "),v("td",[v("code",[_._v("Not")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("!=")])]),_._v(" "),v("td",[v("code",[_._v("var != expr")])]),_._v(" "),v("td",[_._v("不等比较")]),_._v(" "),v("td",[v("code",[_._v("PartialEq")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("%")])]),_._v(" "),v("td",[v("code",[_._v("expr % expr")])]),_._v(" "),v("td",[_._v("算术取模")]),_._v(" "),v("td",[v("code",[_._v("Rem")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("%=")])]),_._v(" "),v("td",[v("code",[_._v("var %= expr")])]),_._v(" "),v("td",[_._v("算术取模与赋值")]),_._v(" "),v("td",[v("code",[_._v("RemAssign")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("&")])]),_._v(" "),v("td",[v("code",[_._v("&expr")]),_._v(", "),v("code",[_._v("&mut expr")])]),_._v(" "),v("td",[_._v("借用")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("&")])]),_._v(" "),v("td",[v("code",[_._v("&type")]),_._v(", "),v("code",[_._v("&mut type")]),_._v(", "),v("code",[_._v("&'a type")]),_._v(", "),v("code",[_._v("&'a mut type")])]),_._v(" "),v("td",[_._v("借用指针类型")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("&")])]),_._v(" "),v("td",[v("code",[_._v("expr & expr")])]),_._v(" "),v("td",[_._v("按位与")]),_._v(" "),v("td",[v("code",[_._v("BitAnd")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("&=")])]),_._v(" "),v("td",[v("code",[_._v("var &= expr")])]),_._v(" "),v("td",[_._v("按位与与及赋值")]),_._v(" "),v("td",[v("code",[_._v("BitAndAssign")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("&&")])]),_._v(" "),v("td",[v("code",[_._v("expr && expr")])]),_._v(" "),v("td",[_._v("逻辑与")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("*")])]),_._v(" "),v("td",[v("code",[_._v("expr * expr")])]),_._v(" "),v("td",[_._v("算术乘法")]),_._v(" "),v("td",[v("code",[_._v("Mul")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("*=")])]),_._v(" "),v("td",[v("code",[_._v("var *= expr")])]),_._v(" "),v("td",[_._v("算术乘法与赋值")]),_._v(" "),v("td",[v("code",[_._v("MulAssign")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("*")])]),_._v(" "),v("td",[v("code",[_._v("*expr")])]),_._v(" "),v("td",[_._v("解引用")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("*")])]),_._v(" "),v("td",[v("code",[_._v("*const type")]),_._v(", "),v("code",[_._v("*mut type")])]),_._v(" "),v("td",[_._v("原生指针")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("+")])]),_._v(" "),v("td",[v("code",[_._v("trait + trait")]),_._v(", "),v("code",[_._v("'a + trait")])]),_._v(" "),v("td",[_._v("复合类型限制")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("+")])]),_._v(" "),v("td",[v("code",[_._v("expr + expr")])]),_._v(" "),v("td",[_._v("算术加法")]),_._v(" "),v("td",[v("code",[_._v("Add")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("+=")])]),_._v(" "),v("td",[v("code",[_._v("var += expr")])]),_._v(" "),v("td",[_._v("算术加法与赋值")]),_._v(" "),v("td",[v("code",[_._v("AddAssign")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v(",")])]),_._v(" "),v("td",[v("code",[_._v("expr, expr")])]),_._v(" "),v("td",[_._v("参数以及元素分隔符")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("-")])]),_._v(" "),v("td",[v("code",[_._v("- expr")])]),_._v(" "),v("td",[_._v("算术取负")]),_._v(" "),v("td",[v("code",[_._v("Neg")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("-")])]),_._v(" "),v("td",[v("code",[_._v("expr - expr")])]),_._v(" "),v("td",[_._v("算术减法")]),_._v(" "),v("td",[v("code",[_._v("Sub")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("-=")])]),_._v(" "),v("td",[v("code",[_._v("var -= expr")])]),_._v(" "),v("td",[_._v("算术减法与赋值")]),_._v(" "),v("td",[v("code",[_._v("SubAssign")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("->")])]),_._v(" "),v("td",[v("code",[_._v("fn(...) -> type")]),_._v(", "),v("code",[_._v("|...| -> type")])]),_._v(" "),v("td",[_._v("函数与闭包,返回类型")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v(".")])]),_._v(" "),v("td",[v("code",[_._v("expr.ident")])]),_._v(" "),v("td",[_._v("成员访问")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("..")])]),_._v(" "),v("td",[v("code",[_._v("..")]),_._v(", "),v("code",[_._v("expr..")]),_._v(", "),v("code",[_._v("..expr")]),_._v(", "),v("code",[_._v("expr..expr")])]),_._v(" "),v("td",[_._v("右排除范围")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("..")])]),_._v(" "),v("td",[v("code",[_._v("..expr")])]),_._v(" "),v("td",[_._v("结构体更新语法")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("..")])]),_._v(" "),v("td",[v("code",[_._v("variant(x, ..)")]),_._v(", "),v("code",[_._v("struct_type { x, .. }")])]),_._v(" "),v("td",[_._v("“与剩余部分”的模式绑定")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("...")])]),_._v(" "),v("td",[v("code",[_._v("expr...expr")])]),_._v(" "),v("td",[_._v("模式: 范围包含模式")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("/")])]),_._v(" "),v("td",[v("code",[_._v("expr / expr")])]),_._v(" "),v("td",[_._v("算术除法")]),_._v(" "),v("td",[v("code",[_._v("Div")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("/=")])]),_._v(" "),v("td",[v("code",[_._v("var /= expr")])]),_._v(" "),v("td",[_._v("算术除法与赋值")]),_._v(" "),v("td",[v("code",[_._v("DivAssign")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v(":")])]),_._v(" "),v("td",[v("code",[_._v("pat: type")]),_._v(", "),v("code",[_._v("ident: type")])]),_._v(" "),v("td",[_._v("约束")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v(":")])]),_._v(" "),v("td",[v("code",[_._v("ident: expr")])]),_._v(" "),v("td",[_._v("结构体字段初始化")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v(":")])]),_._v(" "),v("td",[v("code",[_._v("'a: loop {...}")])]),_._v(" "),v("td",[_._v("循环标志")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v(";")])]),_._v(" "),v("td",[v("code",[_._v("expr;")])]),_._v(" "),v("td",[_._v("语句和语句结束符")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v(";")])]),_._v(" "),v("td",[v("code",[_._v("[...; len]")])]),_._v(" "),v("td",[_._v("固定大小数组语法的部分")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("<<")])]),_._v(" "),v("td",[v("code",[_._v("expr << expr")])]),_._v(" "),v("td",[_._v("左移")]),_._v(" "),v("td",[v("code",[_._v("Shl")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("<<=")])]),_._v(" "),v("td",[v("code",[_._v("var <<= expr")])]),_._v(" "),v("td",[_._v("左移与赋值")]),_._v(" "),v("td",[v("code",[_._v("ShlAssign")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("<")])]),_._v(" "),v("td",[v("code",[_._v("expr < expr")])]),_._v(" "),v("td",[_._v("小于比较")]),_._v(" "),v("td",[v("code",[_._v("PartialOrd")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("<=")])]),_._v(" "),v("td",[v("code",[_._v("expr <= expr")])]),_._v(" "),v("td",[_._v("小于等于比较")]),_._v(" "),v("td",[v("code",[_._v("PartialOrd")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("=")])]),_._v(" "),v("td",[v("code",[_._v("var = expr")]),_._v(", "),v("code",[_._v("ident = type")])]),_._v(" "),v("td",[_._v("赋值/等值")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("==")])]),_._v(" "),v("td",[v("code",[_._v("expr == expr")])]),_._v(" "),v("td",[_._v("等于比较")]),_._v(" "),v("td",[v("code",[_._v("PartialEq")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("=>")])]),_._v(" "),v("td",[v("code",[_._v("pat => expr")])]),_._v(" "),v("td",[_._v("匹配准备语法的部分")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v(">")])]),_._v(" "),v("td",[v("code",[_._v("expr > expr")])]),_._v(" "),v("td",[_._v("大于比较")]),_._v(" "),v("td",[v("code",[_._v("PartialOrd")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v(">=")])]),_._v(" "),v("td",[v("code",[_._v("expr >= expr")])]),_._v(" "),v("td",[_._v("大于等于比较")]),_._v(" "),v("td",[v("code",[_._v("PartialOrd")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v(">>")])]),_._v(" "),v("td",[v("code",[_._v("expr >> expr")])]),_._v(" "),v("td",[_._v("右移")]),_._v(" "),v("td",[v("code",[_._v("Shr")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v(">>=")])]),_._v(" "),v("td",[v("code",[_._v("var >>= expr")])]),_._v(" "),v("td",[_._v("右移与赋值")]),_._v(" "),v("td",[v("code",[_._v("ShrAssign")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("@")])]),_._v(" "),v("td",[v("code",[_._v("ident @ pat")])]),_._v(" "),v("td",[_._v("模式绑定")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("^")])]),_._v(" "),v("td",[v("code",[_._v("expr ^ expr")])]),_._v(" "),v("td",[_._v("按位异或")]),_._v(" "),v("td",[v("code",[_._v("BitXor")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("^=")])]),_._v(" "),v("td",[v("code",[_._v("var ^= expr")])]),_._v(" "),v("td",[_._v("按位异或与赋值")]),_._v(" "),v("td",[v("code",[_._v("BitXorAssign")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("|")])]),_._v(" "),v("td",[v("code",[_._v("pat | pat")])]),_._v(" "),v("td",[_._v("模式选择")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("|")])]),_._v(" "),v("td",[v("code",[_._v("expr | expr")])]),_._v(" "),v("td",[_._v("按位或")]),_._v(" "),v("td",[v("code",[_._v("BitOr")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("|=")])]),_._v(" "),v("td",[v("code",[_._v("var |= expr")])]),_._v(" "),v("td",[_._v("按位或与赋值")]),_._v(" "),v("td",[v("code",[_._v("BitOrAssign")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("||")])]),_._v(" "),v("td",[v("code",[_._v("expr || expr")])]),_._v(" "),v("td",[_._v("逻辑或")]),_._v(" "),v("td")]),_._v(" "),v("tr",[v("td",[v("code",[_._v("?")])]),_._v(" "),v("td",[v("code",[_._v("expr?")])]),_._v(" "),v("td",[_._v("错误传播")]),_._v(" "),v("td")])])])},function(){var _=this.$createElement,t=this._self._c||_;return t("h3",{attrs:{id:"非运算符符号"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#非运算符符号","aria-hidden":"true"}},[this._v("#")]),this._v(" 非运算符符号")])},function(){var _=this.$createElement,t=this._self._c||_;return t("p",[t("span",{staticClass:"caption"},[this._v("表 B-2:独立语法")])])},function(){var _=this,t=_.$createElement,v=_._self._c||t;return v("table",[v("thead",[v("tr",[v("th",[_._v("符号")]),_._v(" "),v("th",[_._v("解释")])])]),_._v(" "),v("tbody",[v("tr",[v("td",[v("code",[_._v("'ident")])]),_._v(" "),v("td",[_._v("命名生命周期或循环标签")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("...u8")]),_._v(", "),v("code",[_._v("...i32")]),_._v(", "),v("code",[_._v("...f64")]),_._v(", "),v("code",[_._v("...usize")]),_._v(", 等")]),_._v(" "),v("td",[_._v("指定类型的数值常量")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v('"..."')])]),_._v(" "),v("td",[_._v("字符串常量")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v('r"..."')]),_._v(", "),v("code",[_._v('r#"..."#')]),_._v(", "),v("code",[_._v('r##"..."##')]),_._v(", etc.")]),_._v(" "),v("td",[_._v("原生字符串常量, 未处理的遗漏字符")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v('b"..."')])]),_._v(" "),v("td",[_._v("字节字符串; 构造一个 "),v("code",[_._v("[u8]")]),_._v(" 类型而非字符串")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v('br"..."')]),_._v(", "),v("code",[_._v('br#"..."#')]),_._v(", "),v("code",[_._v('br##"..."##')]),_._v(", 等")]),_._v(" "),v("td",[_._v("原生字节字符串常量,原生字节和字节结合的字符串")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("'...'")])]),_._v(" "),v("td",[_._v("字符常量")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("b'...'")])]),_._v(" "),v("td",[_._v("ASCII码字节常量")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("|...| expr")])]),_._v(" "),v("td",[_._v("结束")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("!")])]),_._v(" "),v("td",[_._v("对一个离散函数来说最后总是空类型")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("_")])]),_._v(" "),v("td",[_._v("“忽略”模式绑定, 也用于整数常量的可读性")])])])])},function(){var _=this.$createElement,t=this._self._c||_;return t("p",[t("span",{staticClass:"caption"},[this._v("表 B-3: 路径相关语法")])])},function(){var _=this,t=_.$createElement,v=_._self._c||t;return v("table",[v("thead",[v("tr",[v("th",[_._v("符号")]),_._v(" "),v("th",[_._v("解释")])])]),_._v(" "),v("tbody",[v("tr",[v("td",[v("code",[_._v("ident::ident")])]),_._v(" "),v("td",[_._v("命名空间路径")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("::path")])]),_._v(" "),v("td",[_._v("与crate根相关的路径(如一个明确的绝对路径)")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("self::path")])]),_._v(" "),v("td",[_._v("当前模块相关路径(如一个明确相关路径)")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("super::path")])]),_._v(" "),v("td",[_._v("父模块相关路径")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("type::ident")]),_._v(", "),v("code",[_._v("::ident")])]),_._v(" "),v("td",[_._v("相关常量、函数以及类型")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("::...")])]),_._v(" "),v("td",[_._v("不可以被直接命名的相关项类型(如 "),v("code",[_._v("<&T>::...")]),_._v(","),v("code",[_._v("<[T]>::...")]),_._v(", 等)")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("trait::method(...)")])]),_._v(" "),v("td",[_._v("通过命名定义的 trait 来消除方法调用的二义性")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("type::method(...)")])]),_._v(" "),v("td",[_._v("通过命名定义的类型来消除方法调用的二义性")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("::method(...)")])]),_._v(" "),v("td",[_._v("通过命名 trait 和类型来消除方法调用的二义性")])])])])},function(){var _=this.$createElement,t=this._self._c||_;return t("p",[t("span",{staticClass:"caption"},[this._v("表 B-4:泛型")])])},function(){var _=this.$createElement,t=this._self._c||_;return t("thead",[t("tr",[t("th",[this._v("符号")]),this._v(" "),t("th",[this._v("解释")])])])},function(){var _=this.$createElement,t=this._self._c||_;return t("tr",[t("td",[t("code",[this._v("path<...>")])]),this._v(" "),t("td",[this._v("为一个类型中的泛型指定具体参数(如 "),t("code",[this._v("Vec")]),this._v(")")])])},function(){var _=this.$createElement,t=this._self._c||_;return t("td",[t("code",[this._v("path::<...>")]),this._v(", "),t("code",[this._v("method::<...>")])])},function(){var _=this.$createElement,t=this._self._c||_;return t("tr",[t("td",[t("code",[this._v("fn ident<...> ...")])]),this._v(" "),t("td",[this._v("泛型函数定义")])])},function(){var _=this.$createElement,t=this._self._c||_;return t("tr",[t("td",[t("code",[this._v("struct ident<...> ...")])]),this._v(" "),t("td",[this._v("泛型结构体定义")])])},function(){var _=this.$createElement,t=this._self._c||_;return t("tr",[t("td",[t("code",[this._v("enum ident<...> ...")])]),this._v(" "),t("td",[this._v("泛型枚举定义")])])},function(){var _=this.$createElement,t=this._self._c||_;return t("tr",[t("td",[t("code",[this._v("impl<...> ...")])]),this._v(" "),t("td",[this._v("定义泛型实现")])])},function(){var _=this.$createElement,t=this._self._c||_;return t("tr",[t("td",[t("code",[this._v("for<...> type")])]),this._v(" "),t("td",[this._v("高级生命周期限制")])])},function(){var _=this.$createElement,t=this._self._c||_;return t("tr",[t("td",[t("code",[this._v("type")])]),this._v(" "),t("td",[this._v("泛型,其一个或多个相关类型必须被指定为特定类型(如 "),t("code",[this._v("Iterator- ")]),this._v(")")])])},function(){var _=this.$createElement,t=this._self._c||_;return t("p",[t("span",{staticClass:"caption"},[this._v("表 B-5: Trait Bound 约束")])])},function(){var _=this,t=_.$createElement,v=_._self._c||t;return v("table",[v("thead",[v("tr",[v("th",[_._v("符号")]),_._v(" "),v("th",[_._v("解释")])])]),_._v(" "),v("tbody",[v("tr",[v("td",[v("code",[_._v("T: U")])]),_._v(" "),v("td",[_._v("泛型参数 "),v("code",[_._v("T")]),_._v(" 约束于实现了 "),v("code",[_._v("U")]),_._v(" 的类型")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("T: 'a")])]),_._v(" "),v("td",[_._v("泛型 "),v("code",[_._v("T")]),_._v(" 的生命周期必须长于 "),v("code",[_._v("'a")]),_._v("(意味着该类型不能传递包含生命周期短于 "),v("code",[_._v("'a")]),_._v(" 的任何引用)")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("T : 'static")])]),_._v(" "),v("td",[_._v("泛型 "),v("code",[_._v("T")]),_._v(" 包含了除 "),v("code",[_._v("'static")]),_._v(" 之外的非借用引用")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("'b: 'a")])]),_._v(" "),v("td",[_._v("泛型 "),v("code",[_._v("'b")]),_._v(" 生命周期必须长于泛型 "),v("code",[_._v("'a")])])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("T: ?Sized")])]),_._v(" "),v("td",[_._v("使用一个不定大小的泛型类型")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("'a + trait")]),_._v(", "),v("code",[_._v("trait + trait")])]),_._v(" "),v("td",[_._v("复合类型限制")])])])])},function(){var _=this.$createElement,t=this._self._c||_;return t("p",[t("span",{staticClass:"caption"},[this._v("表 B-6: 宏与属性")])])},function(){var _=this,t=_.$createElement,v=_._self._c||t;return v("table",[v("thead",[v("tr",[v("th",[_._v("符号")]),_._v(" "),v("th",[_._v("解释")])])]),_._v(" "),v("tbody",[v("tr",[v("td",[v("code",[_._v("#[meta]")])]),_._v(" "),v("td",[_._v("外部属性")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("#![meta]")])]),_._v(" "),v("td",[_._v("内部属性")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("$ident")])]),_._v(" "),v("td",[_._v("宏替换")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("$ident:kind")])]),_._v(" "),v("td",[_._v("宏捕获")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("$(…)…")])]),_._v(" "),v("td",[_._v("宏重复")])])])])},function(){var _=this.$createElement,t=this._self._c||_;return t("p",[t("span",{staticClass:"caption"},[this._v("表 B-7: 注释")])])},function(){var _=this,t=_.$createElement,v=_._self._c||t;return v("table",[v("thead",[v("tr",[v("th",[_._v("符号")]),_._v(" "),v("th",[_._v("注释")])])]),_._v(" "),v("tbody",[v("tr",[v("td",[v("code",[_._v("//")])]),_._v(" "),v("td",[_._v("行注释")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("//!")])]),_._v(" "),v("td",[_._v("内部行文档注释")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("///")])]),_._v(" "),v("td",[_._v("外部行文档注释")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("/*...*/")])]),_._v(" "),v("td",[_._v("块注释")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("/*!...*/")])]),_._v(" "),v("td",[_._v("内部块文档注释")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("/**...*/")])]),_._v(" "),v("td",[_._v("外部块文档注释")])])])])},function(){var _=this,t=_.$createElement,v=_._self._c||t;return v("table",[v("thead",[v("tr",[v("th",[_._v("符号")]),_._v(" "),v("th",[_._v("解释")])])]),_._v(" "),v("tbody",[v("tr",[v("td",[v("code",[_._v("()")])]),_._v(" "),v("td",[_._v("空元祖(亦称单元), 用于常量或类型中")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("(expr)")])]),_._v(" "),v("td",[_._v("括号表达式")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("(expr,)")])]),_._v(" "),v("td",[_._v("单一元素元组表达式")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("(type,)")])]),_._v(" "),v("td",[_._v("单一元素元组类型")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("(expr, ...)")])]),_._v(" "),v("td",[_._v("元组表达式")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("(type, ...)")])]),_._v(" "),v("td",[_._v("元组类型")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("expr(expr, ...)")])]),_._v(" "),v("td",[_._v("函数调用表达式; 也用于初始化元组结构体 "),v("code",[_._v("struct")]),_._v(" 以及元组枚举 "),v("code",[_._v("enum")]),_._v(" 变体")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("ident!(...)")]),_._v(", "),v("code",[_._v("ident!{...}")]),_._v(", "),v("code",[_._v("ident![...]")])]),_._v(" "),v("td",[_._v("宏调用")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("expr.0")]),_._v(", "),v("code",[_._v("expr.1")]),_._v(", etc.")]),_._v(" "),v("td",[_._v("元组索引")])])])])},function(){var _=this,t=_.$createElement,v=_._self._c||t;return v("table",[v("thead",[v("tr",[v("th",[_._v("符号")]),_._v(" "),v("th",[_._v("解释")])])]),_._v(" "),v("tbody",[v("tr",[v("td",[v("code",[_._v("{...}")])]),_._v(" "),v("td",[_._v("块表达式")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("Type {...}")])]),_._v(" "),v("td",[v("code",[_._v("struct")])])])])])},function(){var _=this.$createElement,t=this._self._c||_;return t("p",[t("span",{staticClass:"caption"},[this._v("表 B-10: 方括号")])])},function(){var _=this,t=_.$createElement,v=_._self._c||t;return v("table",[v("thead",[v("tr",[v("th",[_._v("符号")]),_._v(" "),v("th",[_._v("解释")])])]),_._v(" "),v("tbody",[v("tr",[v("td",[v("code",[_._v("[...]")])]),_._v(" "),v("td",[_._v("数组")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("[expr; len]")])]),_._v(" "),v("td",[_._v("复制了 "),v("code",[_._v("len")]),_._v("个 "),v("code",[_._v("expr")]),_._v("的数组")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("[type; len]")])]),_._v(" "),v("td",[_._v("包含 "),v("code",[_._v("len")]),_._v("个 "),v("code",[_._v("type")]),_._v(" 类型的数组")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("expr[expr]")])]),_._v(" "),v("td",[_._v("集合索引。 重载("),v("code",[_._v("Index")]),_._v(", "),v("code",[_._v("IndexMut")]),_._v(")")])]),_._v(" "),v("tr",[v("td",[v("code",[_._v("expr[..]")]),_._v(", "),v("code",[_._v("expr[a..]")]),_._v(", "),v("code",[_._v("expr[..b]")]),_._v(", "),v("code",[_._v("expr[a..b]")])]),_._v(" "),v("td",[_._v("集合索引,使用 "),v("code",[_._v("Range")]),_._v(","),v("code",[_._v("RangeFrom")]),_._v(","),v("code",[_._v("RangeTo")]),_._v(" 或 "),v("code",[_._v("RangeFull")]),_._v(" 作为索引来代替集合切片")])])])])}],!1,null,null,null);e.options.__file="appendix-02-operators.md";t.default=e.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/70.e265a8a0.js b/src/.vuepress/dist/assets/js/70.e265a8a0.js
new file mode 100644
index 0000000..8f706ce
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/70.e265a8a0.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[70],{200:function(t,e,s){"use strict";s.r(e);var i=s(0),r=Object(i.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/src/ch13-04-performance.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch13-04-performance.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),s("p",[t._v("结果迭代器版本还要稍微快一点!这里我们将不会查看性能测试的代码,我们的目的并不是为了证明他们是完全等同的,而是得出一个怎样比较这两种实现方式性能的基本思路。")]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._m(8),t._v(" "),t._m(9),t._v(" "),s("p",[t._v("所有的系数都被储存在了寄存器中,这意味着访问他们非常快。这里也没有运行时数组访问边界检查。所有这些 Rust 能够提供的优化使得结果代码极为高效。现在知道这些了,请放心大胆的使用迭代器和闭包吧!他们使得代码看起来更高级,但并不为此引入运行时性能损失。")]),t._v(" "),t._m(10),t._v(" "),s("p",[t._v("闭包和迭代器是 Rust 受函数式编程语言观念所启发的功能。他们对 Rust 以底层的性能来明确的表达高级概念的能力有很大贡献。闭包和迭代器的实现达到了不影响运行时性能的程度。这正是 Rust 竭力提供零成本抽象的目标的一部分。")]),t._v(" "),t._m(11)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"性能对比:循环-vs-迭代器"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#性能对比:循环-vs-迭代器","aria-hidden":"true"}},[this._v("#")]),this._v(" 性能对比:循环 VS 迭代器")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("为了决定使用哪个实现,我们需要知道哪个版本的 "),e("code",[this._v("search")]),this._v(" 函数更快:直接使用 "),e("code",[this._v("for")]),this._v(" 循环的版本还是使用迭代器的版本。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("我们运行了一个性能测试,通过将阿瑟·柯南·道尔的“福尔摩斯探案集”的全部内容加载进 "),e("code",[this._v("String")]),this._v(" 并寻找其中的单词 “the”。如下是 "),e("code",[this._v("for")]),this._v(" 循环版本和迭代器版本的 "),e("code",[this._v("search")]),this._v(" 函数的性能测试结果:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)\ntest bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)\n")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("对于一个更全面的性能测试,将会检查不同长度的文本、不同的搜索单词、不同长度的单词和所有其他的可变情况。这里所要表达的是:迭代器,作为一个高级的抽象,被编译成了与手写的底层代码大体一致性能代码。迭代器是 Rust 的 "),s("strong",[t._v("零成本抽象")]),t._v("("),s("em",[t._v("zero-cost abstractions")]),t._v(")之一,它意味着抽象并不会强加运行时开销,它与本贾尼·斯特劳斯特卢普,C++ 的设计和实现者所定义的 "),s("strong",[t._v("零开销")]),t._v("("),s("em",[t._v("zero-overhead")]),t._v(")如出一辙:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("blockquote",[e("p",[this._v("In general, C++ implementations obey the zero-overhead principle: What you don’t use, you don’t pay for. And further: What you do use, you couldn’t hand code any better.")]),this._v(" "),e("ul",[e("li",[this._v('Bjarne Stroustrup "Foundations of C++"')])]),this._v(" "),e("p",[this._v("从整体来说,C++ 的实现遵循了零开销原则:你不需要的,无需为他们买单。更有甚者的是:你需要的时候,也不可能找到其他更好的代码了。")]),this._v(" "),e("ul",[e("li",[this._v('本贾尼·斯特劳斯特卢普 "Foundations of C++"')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("作为另一个例子,这里有一些取自于音频解码器的代码。解码算法使用线性预测数学运算(linear prediction mathematical operation)来根据之前样本的线性函数预测将来的值。这些代码使用迭代器链来对作用域中的三个变量进行了某种数学计算:一个叫 "),e("code",[this._v("buffer")]),this._v(" 的数据 slice、一个有 12 个元素的数组 "),e("code",[this._v("coefficients")]),this._v("、和一个代表位移位数的 "),e("code",[this._v("qlp_shift")]),this._v("。例子中声明了这些变量但并没有提供任何值;虽然这些代码在其上下文之外没有什么意义,不过仍是一个简明的现实中的例子,来展示 Rust 如何将高级概念转换为底层代码:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("let buffer: &mut [i32];\nlet coefficients: [i64; 12];\nlet qlp_shift: i16;\n\nfor i in 12..buffer.len() {\n let prediction = coefficients.iter()\n .zip(&buffer[i - 12..i])\n .map(|(&c, &s)| c * s as i64)\n .sum::
() >> qlp_shift;\n let delta = buffer[i];\n buffer[i] = prediction as i32 + delta;\n}\n")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("为了计算 "),s("code",[t._v("prediction")]),t._v(" 的值,这些代码遍历了 "),s("code",[t._v("coefficients")]),t._v(" 中的 12 个值,使用 "),s("code",[t._v("zip")]),t._v(" 方法将系数与 "),s("code",[t._v("buffer")]),t._v(" 的前 12 个值组合在一起。接着将每一对值相乘,再将所有结果相加,然后将总和右移 "),s("code",[t._v("qlp_shift")]),t._v(" 位。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("像音频解码器这样的程序通常最看重计算的性能。这里,我们创建了一个迭代器,使用了两个适配器,接着消费了其值。Rust 代码将会被编译为什么样的汇编代码呢?好吧,在编写本书的这个时候,它被编译成与手写的相同的汇编代码。遍历 "),e("code",[this._v("coefficients")]),this._v(" 的值完全用不到循环:Rust 知道这里会迭代 12 次,所以它“展开”(unroll)了循环。展开是一种移除循环控制代码的开销并替换为每个迭代中的重复代码的优化。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"总结"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("现在我们改进了我们 I/O 项目的(代码)表现力,让我们看一看更多 "),e("code",[this._v("cargo")]),this._v(" 的功能,他们将帮助我们准备好将项目分享给世界。")])}],!1,null,null,null);r.options.__file="ch13-04-performance.md";e.default=r.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/71.08e24461.js b/src/.vuepress/dist/assets/js/71.08e24461.js
new file mode 100644
index 0000000..4514226
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/71.08e24461.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[71],{199:function(r,t,e){"use strict";e.r(t);var o=e(0),a=Object(o.a)({},function(){var r=this,t=r.$createElement,e=r._self._c||t;return e("div",{staticClass:"content"},[r._m(0),r._v(" "),e("blockquote",[e("p",[e("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch14-00-more-about-cargo.md",target:"_blank",rel:"noopener noreferrer"}},[r._v("ch14-00-more-about-cargo.md"),e("OutboundLink")],1),r._v(" "),e("br"),r._v("\ncommit ff93f82ff63ade5a352d9ccc430945d4ec804cdf")])]),r._v(" "),e("p",[r._v("目前为止我们只使用过 Cargo 构建、运行和测试代码的最基本功能,不过它还可以做到更多。这里我们将了解一些 Cargo 其他更为高级的功能,他们将展示如何:")]),r._v(" "),e("ul",[e("li",[r._v("使用发布配置来自定义构建")]),r._v(" "),e("li",[r._v("将库发布到 "),e("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[r._v("crates.io"),e("OutboundLink")],1)]),r._v(" "),e("li",[r._v("使用工作空间来组织更大的项目")]),r._v(" "),e("li",[r._v("从 "),e("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[r._v("crates.io"),e("OutboundLink")],1),r._v(" 安装二进制文件")]),r._v(" "),e("li",[r._v("使用自定义的命令来扩展 Cargo")])]),r._v(" "),e("p",[r._v("相比本章能够涉及的工作 Cargo 甚至还可以做到更多,关于其功能的全部解释,请查看 "),e("a",{attrs:{href:"http://doc.rust-lang.org/cargo/",target:"_blank",rel:"noopener noreferrer"}},[r._v("其文档"),e("OutboundLink")],1)])])},[function(){var r=this.$createElement,t=this._self._c||r;return t("h1",{attrs:{id:"进一步认识-cargo-和-crates-io"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#进一步认识-cargo-和-crates-io","aria-hidden":"true"}},[this._v("#")]),this._v(" 进一步认识 Cargo 和 Crates.io")])}],!1,null,null,null);a.options.__file="ch14-00-more-about-cargo.md";t.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/72.9f1ed203.js b/src/.vuepress/dist/assets/js/72.9f1ed203.js
new file mode 100644
index 0000000..d3aa323
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/72.9f1ed203.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[72],{198:function(e,t,r){"use strict";r.r(t);var s=r(0),_=Object(s.a)({},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"content"},[e._m(0),e._v(" "),r("blockquote",[r("p",[r("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch14-01-release-profiles.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ch14-01-release-profiles.md"),r("OutboundLink")],1),e._v(" "),r("br"),e._v("\ncommit ff93f82ff63ade5a352d9ccc430945d4ec804cdf")])]),e._v(" "),e._m(1),e._v(" "),e._m(2),e._v(" "),r("p",[e._v("我们应该很熟悉这些配置名称因为他们出现在构建的输出中,这会展示构建所使用的配置:")]),e._v(" "),e._m(3),e._m(4),e._v(" "),e._m(5),e._v(" "),e._m(6),e._v(" "),e._m(7),e._m(8),e._v(" "),e._m(9),e._v(" "),e._m(10),e._v(" "),e._m(11),e._m(12),e._v(" "),r("p",[e._v("对于每个配置的设置和其默认值的完整列表,请查看 "),r("a",{attrs:{href:"https://doc.rust-lang.org/cargo/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Cargo 的文档"),r("OutboundLink")],1),e._v("。")])])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"采用发布配置自定义构建"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#采用发布配置自定义构建","aria-hidden":"true"}},[this._v("#")]),this._v(" 采用发布配置自定义构建")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("在 Rust 中 "),t("strong",[this._v("发布配置")]),this._v("("),t("em",[this._v("release profiles")]),this._v(")是预定义的、可定制的带有不同选项的配置,他们允许程序员更多的控制代码编译的多种选项。每一个配置都彼此相互独立。")])},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("p",[e._v("Cargo 有两个主要的配置:运行 "),r("code",[e._v("cargo build")]),e._v(" 时采用的 "),r("code",[e._v("dev")]),e._v(" 配置和运行 "),r("code",[e._v("cargo build --release")]),e._v(" 的 "),r("code",[e._v("release")]),e._v(" 配置。"),r("code",[e._v("dev")]),e._v(" 配置被定义为开发时的好的默认配置,"),r("code",[e._v("release")]),e._v(" 配置则有着良好的发布构建的默认配置。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-text extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("$ cargo build\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n$ cargo build --release\n Finished release [optimized] target(s) in 0.0 secs\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("构建输出中的 "),t("code",[this._v("dev")]),this._v(" 和 "),t("code",[this._v("release")]),this._v(" 表明编译器在使用不同的配置。")])},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("p",[e._v("Cargo 对每一个配置都有默认设置,当项目的 "),r("em",[e._v("Cargo.toml")]),e._v(" 文件中没有任何 "),r("code",[e._v("[profile.*]")]),e._v(" 部分的时候。通过增加任何希望定制的配置对应的 "),r("code",[e._v("[profile.*]")]),e._v(" 部分,我们可以选择覆盖任意默认设置的子集。例如,如下是 "),r("code",[e._v("dev")]),e._v(" 和 "),r("code",[e._v("release")]),e._v(" 配置的 "),r("code",[e._v("opt-level")]),e._v(" 设置的默认值:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-toml extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("[profile.dev]\nopt-level = 0\n\n[profile.release]\nopt-level = 3\n")])])])},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("p",[r("code",[e._v("opt-level")]),e._v(" 设置控制 Rust 会对代码进行何种程度的优化。这个配置的值从 0 到 3。越高的优化级别需要更多的时间编译,所以如果你在进行开发并经常编译,可能会希望在牺牲一些代码性能的情况下编译得快一些。这就是为什么 "),r("code",[e._v("dev")]),e._v(" 的 "),r("code",[e._v("opt-level")]),e._v(" 默认为 "),r("code",[e._v("0")]),e._v("。当你准备发布时,花费更多时间在编译上则更好。只需要在发布模式编译一次,而编译出来的程序则会运行很多次,所以发布模式用更长的编译时间换取运行更快的代码。这正是为什么 "),r("code",[e._v("release")]),e._v(" 配置的 "),r("code",[e._v("opt-level")]),e._v(" 默认为 "),r("code",[e._v("3")]),e._v("。")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[this._v("我们可以选择通过在 "),t("em",[this._v("Cargo.toml")]),this._v(" 增加不同的值来覆盖任何默认设置。比如,如果我们想要在开发配置中使用级别 1 的优化,则可以在 "),t("em",[this._v("Cargo.toml")]),this._v(" 中增加这两行:")])},function(){var e=this.$createElement,t=this._self._c||e;return t("p",[t("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language-toml extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("[profile.dev]\nopt-level = 1\n")])])])},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("p",[e._v("这会覆盖默认的设置 "),r("code",[e._v("0")]),e._v("。现在运行 "),r("code",[e._v("cargo build")]),e._v(" 时,Cargo 将会使用 "),r("code",[e._v("dev")]),e._v(" 的默认配置加上定制的 "),r("code",[e._v("opt-level")]),e._v("。因为 "),r("code",[e._v("opt-level")]),e._v(" 设置为 "),r("code",[e._v("1")]),e._v(",Cargo 会比默认进行更多的优化,但是没有发布构建那么多。")])}],!1,null,null,null);_.options.__file="ch14-01-release-profiles.md";t.default=_.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/73.cf073a90.js b/src/.vuepress/dist/assets/js/73.cf073a90.js
new file mode 100644
index 0000000..2dce51b
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/73.cf073a90.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[73],{197:function(t,e,s){"use strict";s.r(e);var r=s(0),a=Object(r.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("blockquote",[s("p",[s("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch14-02-publishing-to-crates-io.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch14-02-publishing-to-crates-io.md"),s("OutboundLink")],1),t._v(" "),s("br"),t._v("\ncommit ff93f82ff63ade5a352d9ccc430945d4ec804cdf")])]),t._v(" "),s("p",[t._v("我们曾经在项目中使用 "),s("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),s("OutboundLink")],1),t._v(" 上的包作为依赖,不过你也可以通过发布自己的包来向它人分享代码。"),s("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),s("OutboundLink")],1),t._v(" 用来分发包的源代码,所以它主要托管开源代码。")]),t._v(" "),s("p",[t._v("Rust 和 Cargo 有一些帮助它人更方便找到和使用你发布的包的功能。我们将介绍一些这样的功能,接着讲到如何发布一个包。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),s("img",{staticClass:"center",attrs:{alt:"`my_crate` 的 `add_one` 函数所渲染的文档注释 HTML",src:"img/trpl14-01.png"}}),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),s("p",[t._v("大部分文档注释不需要所有这些部分,不过这是一个提醒你检查调用你代码的人有兴趣了解的内容的列表。")]),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),t._m(21),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._v(" "),s("img",{staticClass:"center",attrs:{alt:"crate 整体注释所渲染的 HTML 文档",src:"img/trpl14-02.png"}}),t._v(" "),t._m(25),t._v(" "),s("p",[t._v("位于项之中的文档注释对于描述 crate 和模块特别有用。使用他们描述其容器整体的目的来帮助 crate 用户理解你的代码组织。")]),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),s("p",[t._v("公有 API 的结构是你发布 crate 时主要需要考虑的。crate 用户没有你那么熟悉其结构,并且如果模块层级过大他们可能会难以找到所需的部分。")]),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._m(32),t._v(" "),t._m(33),t._v(" "),s("img",{staticClass:"center",attrs:{alt:"包含 `kinds` 和 `utils` 模块的 `art`",src:"img/trpl14-03.png"}}),t._v(" "),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),t._v(" "),t._m(43),t._m(44),t._v(" "),t._m(45),t._v(" "),s("img",{staticClass:"center",attrs:{alt:"Rendered documentation for the `art` crate with the re-exports on the front page",src:"img/trpl14-04.png"}}),t._v(" "),t._m(46),t._v(" "),t._m(47),t._v(" "),t._m(48),t._v(" "),t._m(49),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._v(" "),t._m(53),t._v(" "),s("p",[t._v("在你可以发布任何 crate 之前,需要在 "),s("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),s("OutboundLink")],1),t._v(" 上注册账号并获取一个 API token。为此,访问位于 "),s("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),s("OutboundLink")],1),t._v(" 的首页并使用 GitHub 账号登陆————目前 GitHub 账号是必须的,不过将来该网站可能会支持其他创建账号的方法。一旦登陆之后,查看位于 "),s("a",{attrs:{href:"https://crates.io/me/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://crates.io/me/"),s("OutboundLink")],1),t._v(" 的账户设置页面并获取 API token。接着使用该 API token 运行 "),s("code",[t._v("cargo login")]),t._v(" 命令,像这样:")]),t._v(" "),t._m(54),s("p",[t._v("这个命令会通知 Cargo 你的 API token 并将其储存在本地的 "),s("em",[t._v("~/.cargo/credentials")]),t._v(" 文件中。注意这个 token 是一个 "),s("strong",[t._v("秘密")]),t._v("("),s("strong",[t._v("secret")]),t._v(")且不应该与其他人共享。如果因为任何原因与他人共享了这个信息,应该立即到 "),s("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),s("OutboundLink")],1),t._v(" 重新生成这个 token。")]),t._v(" "),t._m(55),t._v(" "),t._m(56),t._v(" "),s("p",[t._v("首先 crate 需要一个唯一的名称。虽然在本地开发 crate 时,可以使用任何你喜欢的名称。不过 "),s("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),s("OutboundLink")],1),t._v(" 上的 crate 名称遵守先到先得的分配原则。一旦某个 crate 名称被使用,其他人就不能再发布这个名称的 crate 了。请在网站上搜索你希望使用的名称来找出它是否已被使用。如果没有,修改 "),s("em",[t._v("Cargo.toml")]),t._v(" 中 "),s("code",[t._v("[package]")]),t._v(" 里的名称为你希望用于发布的名称,像这样:")]),t._v(" "),t._m(57),t._v(" "),t._m(58),t._m(59),t._v(" "),t._m(60),t._m(61),t._v(" "),t._m(62),t._v(" "),t._m(63),t._v(" "),t._m(64),t._m(65),t._v(" "),t._m(66),t._v(" "),t._m(67),t._v(" "),t._m(68),t._v(" "),t._m(69),s("p",[s("a",{attrs:{href:"http://doc.rust-lang.org/cargo/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Cargo 的文档"),s("OutboundLink")],1),t._v(" 描述了其他可以指定的元信息,他们可以帮助你的 crate 更容易被发现和使用!")]),t._v(" "),t._m(70),t._v(" "),s("p",[t._v("现在我们创建了一个账号,保存了 API token,为 crate 选择了一个名字,并指定了所需的元数据,你已经准备好发布了!发布 crate 会上传特定版本的 crate 到 "),s("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),s("OutboundLink")],1),t._v(" 以供他人使用。")]),t._v(" "),s("p",[t._v("发布 crate 时请多加小心,因为发布是 "),s("strong",[t._v("永久性的")]),t._v("("),s("em",[t._v("permanent")]),t._v(")。对应版本不可能被覆盖,其代码也不可能被删除。"),s("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),s("OutboundLink")],1),t._v(" 的一个主要目标是作为一个代码的永久文档服务器,这样所有依赖 "),s("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),s("OutboundLink")],1),t._v(" 中 crate 的项目都能一直正常工作。允许删除版本将不可能满足这个目标。然而,可以被发布的版本号却没有限制。")]),t._v(" "),t._m(71),t._v(" "),t._m(72),s("p",[t._v("恭喜!你现在向 Rust 社区分享了代码,而且任何人都可以轻松的将你的 crate 加入他们项目的依赖。")]),t._v(" "),t._m(73),t._v(" "),s("p",[t._v("当你修改了 crate 并准备好发布新版本时,改变 "),s("em",[t._v("Cargo.toml")]),t._v(" 中 "),s("code",[t._v("version")]),t._v(" 所指定的值。请使用 "),s("a",{attrs:{href:"http://semver.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("语义化版本规则"),s("OutboundLink")],1),t._v(" 来根据修改的类型决定下一个版本号。接着运行 "),s("code",[t._v("cargo publish")]),t._v(" 来上传新版本。")]),t._v(" "),t._m(74),t._v(" "),t._m(75),t._v(" "),t._m(76),t._v(" "),t._m(77),t._v(" "),t._m(78),t._m(79),t._v(" "),t._m(80),t._m(81)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"将-crate-发布到-crates-io"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#将-crate-发布到-crates-io","aria-hidden":"true"}},[this._v("#")]),this._v(" 将 crate 发布到 Crates.io")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"编写有用的文档注释"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#编写有用的文档注释","aria-hidden":"true"}},[this._v("#")]),this._v(" 编写有用的文档注释")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("准确的包文档有助于其他用户理解如何以及何时使用他们,所以花一些时间编写文档是值得的。第三章中我们讨论了如何使用 "),s("code",[t._v("//")]),t._v(" 注释 Rust 代码。Rust 也有特定的用于文档的注释类型,通常被称为 "),s("strong",[t._v("文档注释")]),t._v("("),s("em",[t._v("documentation comments")]),t._v("),他们会生成 HTML 文档。这些 HTML 展示公有 API 文档注释的内容,他们意在让对库感兴趣的程序员理解如何 "),s("strong",[t._v("使用")]),t._v(" 这个 crate,而不是它是如何被 "),s("strong",[t._v("实现")]),t._v(" 的。")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("文档注释使用 "),s("code",[t._v("///")]),t._v(" 而不是 "),s("code",[t._v("//")]),t._v(" 并支持 Markdown 注解来格式化文本。文档注释就位于需要文档的项的之前。示例 14-1 展示了一个 "),s("code",[t._v("my_crate")]),t._v(" crate 中 "),s("code",[t._v("add_one")]),t._v(" 函数的文档注释:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("/// Adds one to the number given.\n///\n/// # Examples\n///\n/// ```\n/// let five = 5;\n///\n/// assert_eq!(6, my_crate::add_one(5));\n/// ```\npub fn add_one(x: i32) -> i32 {\n x + 1\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 14-1:一个函数的文档注释")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("这里,我们提供了一个 "),s("code",[t._v("add_one")]),t._v(" 函数工作的描述,接着开始了一个标题为 “Examples” 的部分,和展示如何使用 "),s("code",[t._v("add_one")]),t._v(" 函数的代码。可以运行 "),s("code",[t._v("cargo doc")]),t._v(" 来生成这个文档注释的 HTML 文档。这个命令运行由 Rust 分发的工具 "),s("code",[t._v("rustdoc")]),t._v(" 并将生成的 HTML 文档放入 "),s("em",[t._v("target/doc")]),t._v(" 目录。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("为了方便起见,运行 "),e("code",[this._v("cargo doc --open")]),this._v(" 会构建当前 crate 文档(同时还有所有 crate 依赖的文档)的 HTML 并在浏览器中打开。导航到 "),e("code",[this._v("add_one")]),this._v(" 函数将会发现文档注释的文本是如何渲染的,如图 14-1 所示:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("图 14-1:"),e("code",[this._v("add_one")]),this._v(" 函数的文档注释 HTML")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"常用(文档注释)部分"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#常用(文档注释)部分","aria-hidden":"true"}},[this._v("#")]),this._v(" 常用(文档注释)部分")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("示例 14-1 中使用了 "),e("code",[this._v("# Examples")]),this._v(" Markdown 标题在 HTML 中创建了一个以 “Examples” 为标题的部分。其他一些 crate 作者经常在文档注释中使用的部分有:")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("ul",[s("li",[s("strong",[t._v("Panics")]),t._v(":这个函数可能会 "),s("code",[t._v("panic!")]),t._v(" 的场景。并不希望程序崩溃的函数调用者应该确保他们不会在这些情况下调用此函数。")]),t._v(" "),s("li",[s("strong",[t._v("Errors")]),t._v(":如果这个函数返回 "),s("code",[t._v("Result")]),t._v(",此部分描述可能会出现何种错误以及什么情况会造成这些错误,这有助于调用者编写代码来采用不同的方式处理不同的错误。")]),t._v(" "),s("li",[s("strong",[t._v("Safety")]),t._v(":如果这个函数使用 "),s("code",[t._v("unsafe")]),t._v(" 代码(这会在第十九章讨论),这一部分应该会涉及到期望函数调用者支持的确保 "),s("code",[t._v("unsafe")]),t._v(" 块中代码正常工作的不变条件(invariants)。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"文档注释作为测试"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#文档注释作为测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 文档注释作为测试")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("在文档注释中增加示例代码块是一个清楚的表明如何使用库的方法,这么做还有一个额外的好处:"),e("code",[this._v("cargo test")]),this._v(" 也会像测试那样运行文档中的示例代码!没有什么比有例子的文档更好的了!也没有什么比不能正常工作的例子更糟的了,因为代码在编写文档时已经改变。尝试 "),e("code",[this._v("cargo test")]),this._v(" 运行像示例 14-1 中 "),e("code",[this._v("add_one")]),this._v(" 函数的文档;应该在测试结果中看到像这样的部分:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(" Doc-tests my_crate\n\nrunning 1 test\ntest src/lib.rs - add_one (line 5) ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("现在尝试改变函数或例子来使例子中的 "),e("code",[this._v("assert_eq!")]),this._v(" 产生 panic。再次运行 "),e("code",[this._v("cargo test")]),this._v(",你将会看到文档测试捕获到了例子与代码不再同步!")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"注释包含项的结构"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#注释包含项的结构","aria-hidden":"true"}},[this._v("#")]),this._v(" 注释包含项的结构")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("还有另一种风格的文档注释,"),e("code",[this._v("//!")]),this._v(",这为包含注释的项,而不是注释之后的项增加文档。这通常用于 crate 根文件(通常是 "),e("em",[this._v("src/lib.rs")]),this._v(")或模块的根文件为 crate 或模块整体提供文档。")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("作为一个例子,如果我们希望增加描述包含 "),s("code",[t._v("add_one")]),t._v(" 函数的 "),s("code",[t._v("my_crate")]),t._v(" crate 目的的文档,可以在 "),s("em",[t._v("src/lib.rs")]),t._v(" 开头增加以 "),s("code",[t._v("//!")]),t._v(" 开头的注释,如示例 14-2 所示:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("//! # My Crate\n//!\n//! `my_crate` is a collection of utilities to make performing certain\n//! calculations more convenient.\n\n/// Adds one to the number given.\n// --snip--\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 14-2:"),e("code",[this._v("my_crate")]),this._v(" crate 整体的文档")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("注意 "),s("code",[t._v("//!")]),t._v(" 的最后一行之后没有任何代码。因为他们以 "),s("code",[t._v("//!")]),t._v(" 开头而不是 "),s("code",[t._v("///")]),t._v(",这是属于包含此注释的项而不是注释之后项的文档。在这个情况中,包含这个注释的项是 "),s("em",[t._v("src/lib.rs")]),t._v(" 文件,也就是 crate 根文件。这些注释描述了整个 crate。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("如果运行 "),e("code",[this._v("cargo doc --open")]),this._v(",将会发现这些注释显示在 "),e("code",[this._v("my_crate")]),this._v(" 文档的首页,位于 crate 中公有项列表之上,如图 14-2 所示:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("图 14-2:包含 "),e("code",[this._v("my_crate")]),this._v(" 整体描述的注释所渲染的文档")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"使用-pub-use-导出合适的公有-api"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#使用-pub-use-导出合适的公有-api","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),e("code",[this._v("pub use")]),this._v(" 导出合适的公有 API")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("第七章介绍了如何使用 "),s("code",[t._v("mod")]),t._v(" 关键字来将代码组织进模块中,如何使用 "),s("code",[t._v("pub")]),t._v(" 关键字将项变为公有,和如何使用 "),s("code",[t._v("use")]),t._v(" 关键字将项引入作用域。然而对你开发来说很有道理的结果可能对用户来说就不太方便了。你可能希望将结构组织进有多个层次的层级中,不过想要使用被定义在很深层级中的类型的人可能很难发现这些类型是否存在。他们也可能会厌烦 "),s("code",[t._v("use my_crate::some_module::another_module::UsefulType;")]),t._v(" 而不是 "),s("code",[t._v("use my_crate::UsefulType;")]),t._v(" 来使用类型。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("好消息是,如果结果对于用户来说 "),e("strong",[this._v("不是")]),this._v(" 很方便,你也无需重新安排内部组织:你可以选择使用 "),e("code",[this._v("pub use")]),this._v(" 重导出(re-export)项来使公有结构不同于私有结构。重导出获取位于一个位置的公有项并将其公开到另一个位置,好像它就定义在这个新位置一样。")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("例如,假设我们创建了一个模块化了充满艺术化气息的库 "),s("code",[t._v("art")]),t._v("。在这个库中是一个包含两个枚举 "),s("code",[t._v("PrimaryColor")]),t._v(" 和 "),s("code",[t._v("SecondaryColor")]),t._v(" 的模块 "),s("code",[t._v("kinds")]),t._v(",以及一个包含函数 "),s("code",[t._v("mix")]),t._v(" 的模块 "),s("code",[t._v("utils")]),t._v(",如示例 14-3 所示:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("//! # Art\n//!\n//! A library for modeling artistic concepts.\n\npub mod kinds {\n /// The primary colors according to the RYB color model.\n pub enum PrimaryColor {\n Red,\n Yellow,\n Blue,\n }\n\n /// The secondary colors according to the RYB color model.\n pub enum SecondaryColor {\n Orange,\n Green,\n Purple,\n }\n}\n\npub mod utils {\n use kinds::*;\n\n /// Combines two primary colors in equal amounts to create\n /// a secondary color.\n pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {\n // --snip--\n }\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 14-3:一个库 "),e("code",[this._v("art")]),this._v(" 其组织包含 "),e("code",[this._v("kinds")]),this._v(" 和 "),e("code",[this._v("utils")]),this._v(" 模块")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("code",[this._v("cargo doc")]),this._v(" 所生成的 crate 文档首页如图 14-3 所示:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("图 14-3:包含 "),e("code",[this._v("kinds")]),this._v(" 和 "),e("code",[this._v("utils")]),this._v(" 模块的库 "),e("code",[this._v("art")]),this._v(" 的文档首页")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("注意 "),s("code",[t._v("PrimaryColor")]),t._v(" 和 "),s("code",[t._v("SecondaryColor")]),t._v(" 类型没有在首页中列出,"),s("code",[t._v("mix")]),t._v(" 函数也是。必须点击 "),s("code",[t._v("kinds")]),t._v(" 或 "),s("code",[t._v("utils")]),t._v(" 才能看到他们。")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("另一个依赖这个库的 crate 需要 "),s("code",[t._v("use")]),t._v(" 语句来导入 "),s("code",[t._v("art")]),t._v(" 中的项,这包含指定其当前定义的模块结构。示例 14-4 展示了一个使用 "),s("code",[t._v("art")]),t._v(" crate 中 "),s("code",[t._v("PrimaryColor")]),t._v(" 和 "),s("code",[t._v("mix")]),t._v(" 项的 crate 的例子:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("extern crate art;\n\nuse art::kinds::PrimaryColor;\nuse art::utils::mix;\n\nfn main() {\n let red = PrimaryColor::Red;\n let yellow = PrimaryColor::Yellow;\n mix(red, yellow);\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 14-4:一个通过导出内部结构使用 "),e("code",[this._v("art")]),this._v(" crate 中项的 crate")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("示例 14-4 中使用 "),s("code",[t._v("art")]),t._v(" crate 代码的作者不得不搞清楚 "),s("code",[t._v("PrimaryColor")]),t._v(" 位于 "),s("code",[t._v("kinds")]),t._v(" 模块而 "),s("code",[t._v("mix")]),t._v(" 位于 "),s("code",[t._v("utils")]),t._v(" 模块。"),s("code",[t._v("art")]),t._v(" crate 的模块结构相比使用它的开发者来说对编写它的开发者更有意义。其内部的 "),s("code",[t._v("kinds")]),t._v(" 模块和 "),s("code",[t._v("utils")]),t._v(" 模块的组织结构并没有对尝试理解如何使用它的人提供任何有价值的信息。"),s("code",[t._v("art")]),t._v(" crate 的模块结构因不得不搞清楚所需的内容在何处和必须在 "),s("code",[t._v("use")]),t._v(" 语句中指定模块名称而显得混乱和不便。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("为了从公有 API 中去掉 crate 的内部组织,我们可以采用示例 14-3 中的 "),e("code",[this._v("art")]),this._v(" crate 并增加 "),e("code",[this._v("pub use")]),this._v(" 语句来重导出项到顶层结构,如示例 14-5 所示:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/lib.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("//! # Art\n//!\n//! A library for modeling artistic concepts.\n\npub use kinds::PrimaryColor;\npub use kinds::SecondaryColor;\npub use utils::mix;\n\npub mod kinds {\n // --snip--\n}\n\npub mod utils {\n // --snip--\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 14-5:增加 "),e("code",[this._v("pub use")]),this._v(" 语句重导出项")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("现在此 crate 由 "),e("code",[this._v("cargo doc")]),this._v(" 生成的 API 文档会在首页列出重导出的项以及其链接,如图 14-4 所示,这就使得这些类型易于查找。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("图 14-10:"),e("code",[this._v("art")]),this._v(" 文档的首页,这里列出了重导出的项")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("code",[this._v("art")]),this._v(" crate 的用户仍然可以看见和选择使用示例 14-3 中的内部结构,或者可以使用示例 14-4 中更为方便的结构,如示例 14-6 所示:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: src/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("extern crate art;\n\nuse art::PrimaryColor;\nuse art::mix;\n\nfn main() {\n // --snip--\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 14-6:一个使用 "),e("code",[this._v("art")]),this._v(" crate 中重导出项的程序")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("对于有很多嵌套模块的情况,使用 "),e("code",[this._v("pub use")]),this._v(" 将类型重导出到顶级结构对于使用 crate 的人来说将会是大为不同的体验。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("创建一个有用的公有 API 结构更像是一门艺术而非科学,你可以反复检视他们来找出最适合用户的 API。选择 "),e("code",[this._v("pub use")]),this._v(" 提供了解耦组织 crate 内部结构和与终端用户体现的灵活性。观察一些你所安装的 crate 的代码来看看其内部结构是否不同于公有 API。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"创建-crates-io-账号"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#创建-crates-io-账号","aria-hidden":"true"}},[this._v("#")]),this._v(" 创建 Crates.io 账号")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo login abcdefghijklmnopqrstuvwxyz012345\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"发布新-crate-之前"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#发布新-crate-之前","aria-hidden":"true"}},[this._v("#")]),this._v(" 发布新 crate 之前")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("有了账号之后,比如说你已经有一个希望发布的 crate。在发布之前,你需要在 crate 的 "),e("em",[this._v("Cargo.toml")]),this._v(" 文件的 "),e("code",[this._v("[package]")]),this._v(" 部分增加一些本 crate 的元信息(metadata)。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-toml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[package]\nname = "guessing_game"\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("即使你选择了一个唯一的名称,如果此时尝试运行 "),e("code",[this._v("cargo publish")]),this._v(" 发布该 crate 的话,会得到一个一个警告接着是一个错误:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo publish\n Updating registry `https://github.com/rust-lang/crates.io-index`\nwarning: manifest has no description, license, license-file, documentation,\nhomepage or repository.\n--snip--\nerror: api errors: missing or empty metadata fields: description, license.\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("这是因为我们缺少一些关键信息:关于该 crate 用途的描述和用户可能在何种条款下使用该 crate 的 license。为了修正这个错误,需要在 "),e("em",[this._v("Cargo.toml")]),this._v(" 中引入这些信息。")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("p",[t._v("描述通常是一两句话,因为它会出现在 crate 的搜索结果中和 crate 页面里。对于 "),s("code",[t._v("license")]),t._v(" 字段,你需要一个 "),s("strong",[t._v("license 标识符值")]),t._v("("),s("em",[t._v("license identifier value")]),t._v(")。Linux 基金会位于 "),s("em",[t._v("http://spdx.org/licenses/")]),t._v(" 的 Software Package Data Exchange (SPDX) 列出了可以使用的标识符。例如,为了指定 crate 使用 MIT License,增加 "),s("code",[t._v("MIT")]),t._v(" 标识符:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-toml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[package]\nname = "guessing_game"\nlicense = "MIT"\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("如果你希望使用不存在于 SPDX 的 license,则需要将 license 文本放入一个文件,将该文件包含进项目中,接着使用 "),e("code",[this._v("license-file")]),this._v(" 来指定文件名而不是使用 "),e("code",[this._v("license")]),this._v(" 字段。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("关于项目所适用的 license 指导超出了本书的范畴。很多 Rust 社区成员选择与 Rust 自身相同的 license,这是一个双许可的 "),e("code",[this._v("MIT OR Apache-2.0")]),this._v(" ———— 这展示了也可以通过 "),e("code",[this._v("OR")]),this._v(" 来分隔来为项目指定多个 license 标识符。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("那么,有了唯一的名称、版本号、由 "),e("code",[this._v("cargo new")]),this._v(" 新建项目时增加的作者信息、描述和所选择的 license,已经准备好发布的项目的 "),e("em",[this._v("Cargo.toml")]),this._v(" 文件可能看起来像这样:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-toml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[package]\nname = "guessing_game"\nversion = "0.1.0"\nauthors = ["Your Name "]\ndescription = "A fun game where you guess what number the computer has chosen."\nlicense = "MIT OR Apache-2.0"\n\n[dependencies]\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"发布到-crates-io"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#发布到-crates-io","aria-hidden":"true"}},[this._v("#")]),this._v(" 发布到 Crates.io")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("再次运行 "),e("code",[this._v("cargo publish")]),this._v(" 命令。这次它应该会成功:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo publish\n Updating registry `https://github.com/rust-lang/crates.io-index`\nPackaging guessing_game v0.1.0 (file:///projects/guessing_game)\nVerifying guessing_game v0.1.0 (file:///projects/guessing_game)\nCompiling guessing_game v0.1.0\n(file:///projects/guessing_game/target/package/guessing_game-0.1.0)\n Finished dev [unoptimized + debuginfo] target(s) in 0.19 secs\nUploading guessing_game v0.1.0 (file:///projects/guessing_game)\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"发布现存-crate-的新版本"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#发布现存-crate-的新版本","aria-hidden":"true"}},[this._v("#")]),this._v(" 发布现存 crate 的新版本")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"使用-cargo-yank-从-crates-io-撤回版本"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#使用-cargo-yank-从-crates-io-撤回版本","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),e("code",[this._v("cargo yank")]),this._v(" 从 Crates.io 撤回版本")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("虽然你不能删除之前版本的 crate,但是可以阻止任何将来的项目将他们加入到依赖中。这在某个版本因为这样或那样的原因被破坏的情况很有用。对于这种情况,Cargo 支持 "),e("strong",[this._v("撤回")]),this._v("("),e("em",[this._v("yanking")]),this._v(")某个版本。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("撤回某个版本会阻止新项目开始依赖此版本,不过所有现存此依赖的项目仍然能够下载和依赖这个版本。从本质上说,撤回意味着所有带有 "),e("em",[this._v("Cargo.lock")]),this._v(" 的项目的依赖不会被破坏,同时任何新生成的 "),e("em",[this._v("Cargo.lock")]),this._v(" 将不能使用被撤回的版本。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("为了撤回一个 crate,运行 "),e("code",[this._v("cargo yank")]),this._v(" 并指定希望撤回的版本:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo yank --vers 1.0.1\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("也可以撤销撤回操作,并允许项目可以再次开始依赖某个版本,通过在命令上增加 "),e("code",[this._v("--undo")]),this._v(":")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo yank --vers 1.0.1 --undo\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("撤回 "),e("strong",[this._v("并没有")]),this._v(" 删除任何代码。举例来说,撤回功能并不意在删除不小心上传的秘密信息。如果出现了这种情况,请立即重新设置这些秘密信息。")])}],!1,null,null,null);a.options.__file="ch14-02-publishing-to-crates-io.md";e.default=a.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/74.e5e6cb7a.js b/src/.vuepress/dist/assets/js/74.e5e6cb7a.js
new file mode 100644
index 0000000..2f08a5f
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/74.e5e6cb7a.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[74],{196:function(t,e,a){"use strict";a.r(e);var s=a(0),n=Object(s.a)({},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("blockquote",[a("p",[a("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch14-03-cargo-workspaces.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch14-03-cargo-workspaces.md"),a("OutboundLink")],1),t._v(" "),a("br"),t._v("\ncommit a59537604248f2970e0831d5ead9f6fac2cdef84")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._m(23),t._v(" "),t._m(24),t._v(" "),t._m(25),a("p",[t._v("工作空间中的 crate 不必相互依赖,所以仍需显式地表明工作空间中 crate 的依赖关系。")]),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._m(32),t._v(" "),t._m(33),t._m(34),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),t._m(37),t._v(" "),t._m(38),t._m(39),t._v(" "),t._m(40),t._m(41),t._v(" "),t._m(42),t._m(43),t._v(" "),t._m(44),t._v(" "),t._m(45),t._v(" "),t._m(46),t._v(" "),t._m(47),t._m(48),t._v(" "),t._m(49),t._m(50),t._v(" "),t._m(51),t._v(" "),t._m(52),t._m(53),t._v(" "),t._m(54),t._v(" "),t._m(55),t._v(" "),a("p",[t._v("随着项目增长,考虑使用工作空间:每一个更小的组件比一大块代码要容易理解。将 crate 保持在工作空间中更易于协调他们的改变,如果他们一起运行并经常需要同时被修改的话。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"cargo-工作空间"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#cargo-工作空间","aria-hidden":"true"}},[this._v("#")]),this._v(" Cargo 工作空间")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("第十二章中,我们构建一个包含二进制 crate 和库 crate 的包。你可能会发现,随着项目开发的深入,库 crate 持续增大,而你希望将其进一步拆分成多个库 crate。对于这种情况,Cargo 提供了一个叫 "),e("strong",[this._v("工作空间")]),this._v("("),e("em",[this._v("workspaces")]),this._v(")的功能,它可以帮助我们管理多个相关的协同开发的包。")])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[a("strong",[t._v("工作空间")]),t._v(" 是一系列共享同样的 "),a("em",[t._v("Cargo.lock")]),t._v(" 和输出目录的包。让我们使用工作空间创建一个项目,这里采用常见的代码这样就可以关注工作空间的结构了。有多种组织工作空间的方式;我们将展示一个常用方法。我们的工作空间有一个二进制项目和两个库。二进制项目会提供作为命令行工具的主要功能,它会依赖另两个库。一个库会提供 "),a("code",[t._v("add_one")]),t._v(" 方法而第二个会提供 "),a("code",[t._v("add_two")]),t._v(" 方法。这三个 crate 将会是相同工作空间的一部分。让我们以新建工作空间目录开始:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ mkdir add\n$ cd add\n")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("在 add* 目录中,创建 "),a("em",[t._v("Cargo.toml")]),t._v(" 文件。这个 "),a("em",[t._v("Cargo.toml")]),t._v(" 文件配置了整个工作空间。它不会包含 "),a("code",[t._v("[package]")]),t._v(" 或其他我们在 "),a("em",[t._v("Cargo.toml")]),t._v(" 中见过的元信息。相反,它以 "),a("code",[t._v("[workspace]")]),t._v(" 部分作为开始,并通过指定 "),a("em",[t._v("adder")]),t._v(" 的路径来为工作空间增加成员,如下会加入二进制 crate:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-toml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[workspace]\n\nmembers = [\n "adder",\n]\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("接下来,在 "),e("em",[this._v("add")]),this._v(" 目录运行 "),e("code",[this._v("cargo new")]),this._v(" 新建 "),e("code",[this._v("adder")]),this._v(" 二进制 crate:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo new --bin adder\n Created binary (application) `adder` project\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("到此为止,可以运行 "),e("code",[this._v("cargo build")]),this._v(" 来构建工作空间。"),e("em",[this._v("add")]),this._v(" 目录中的文件应该看起来像这样:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("├── Cargo.lock\n├── Cargo.toml\n├── adder\n│ ├── Cargo.toml\n│ └── src\n│ └── main.rs\n└── target\n")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("工作空间在顶级目录有一个 "),a("em",[t._v("target")]),t._v(" 目录;"),a("code",[t._v("adder")]),t._v(" 并没有自己的 "),a("em",[t._v("target")]),t._v(" 目录。即使进入 "),a("em",[t._v("adder")]),t._v(" 目录运行 "),a("code",[t._v("cargo build")]),t._v(",构建结果也位于 "),a("em",[t._v("add/target")]),t._v(" 而不是 "),a("em",[t._v("add/adder/target")]),t._v("。工作空间中的 crate 之间相互依赖。如果每个 crate 有其自己的 "),a("em",[t._v("target")]),t._v(" 目录,为了在自己的 "),a("em",[t._v("target")]),t._v(" 目录中生成构建结果,工作空间中的每一个 crate 都不得不相互重新编译其他 crate。通过共享一个 "),a("em",[t._v("target")]),t._v(" 目录,工作空间可以避免其他 crate 多余的重复构建。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"在工作空间中创建第二个-crate"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#在工作空间中创建第二个-crate","aria-hidden":"true"}},[this._v("#")]),this._v(" 在工作空间中创建第二个 crate")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("接下来,让我们在工作空间中指定另一个成员 crate。这个 crate 位于 "),e("em",[this._v("add-one")]),this._v(" 目录中,所以修改顶级 "),e("em",[this._v("Cargo.toml")]),this._v(" 为也包含 "),e("em",[this._v("add-one")]),this._v(" 路径:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: Cargo.toml")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-toml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[workspace]\n\nmembers = [\n "adder",\n "add-one",\n]\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("接着新生成一个叫做 "),e("code",[this._v("add-one")]),this._v(" 的库:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo new add-one\n Created library `add-one` project\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("现在 "),e("em",[this._v("add")]),this._v(" 目录应该有如下目录和文件:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("├── Cargo.lock\n├── Cargo.toml\n├── add-one\n│ ├── Cargo.toml\n│ └── src\n│ └── lib.rs\n├── adder\n│ ├── Cargo.toml\n│ └── src\n│ └── main.rs\n└── target\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("在 "),e("em",[this._v("add-one/src/lib.rs")]),this._v(" 文件中,增加一个 "),e("code",[this._v("add_one")]),this._v(" 函数:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: add-one/src/lib.rs")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("add_one")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("现在工作空间中有了一个库 crate,让 "),a("code",[t._v("adder")]),t._v(" 依赖库 crate "),a("code",[t._v("add-one")]),t._v("。首先需要在 "),a("em",[t._v("adder/Cargo.toml")]),t._v(" 文件中增加 "),a("code",[t._v("add-one")]),t._v(" 作为路径依赖:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: adder/Cargo.toml")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-toml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[dependencies]\n\nadd-one = { path = "../add-one" }\n')])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("接下来,在 "),a("code",[t._v("adder")]),t._v(" crate 中使用 "),a("code",[t._v("add-one")]),t._v(" crate 的函数 "),a("code",[t._v("add_one")]),t._v("。打开 "),a("em",[t._v("adder/src/main.rs")]),t._v(" 在顶部增加一行 "),a("code",[t._v("extern crate")]),t._v(" 将新 "),a("code",[t._v("add-one")]),t._v(" 库 crate 引入作用域。接着修改 "),a("code",[t._v("main")]),t._v(" 函数来调用 "),a("code",[t._v("add_one")]),t._v(" 函数,如示例 14-7 所示:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: adder/src/main.rs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-rust,ignore extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('extern crate add_one;\n\nfn main() {\n let num = 10;\n println!("Hello, world! {} plus one is {}!", num, add_one::add_one(num));\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"caption"},[this._v("示例 14-7:在 "),e("code",[this._v("adder")]),this._v(" crate 中使用 "),e("code",[this._v("add-one")]),this._v(" 库 crate")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("在 "),e("em",[this._v("add")]),this._v(" 目录中运行 "),e("code",[this._v("cargo build")]),this._v(" 来构建工作空间!")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo build\n Compiling add-one v0.1.0 (file:///projects/add/add-one)\n Compiling adder v0.1.0 (file:///projects/add/adder)\n Finished dev [unoptimized + debuginfo] target(s) in 0.68 secs\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("为了在顶层 "),e("em",[this._v("add")]),this._v(" 目录运行二进制 crate,需要通过 "),e("code",[this._v("-p")]),this._v(" 参数和包名称来运行 "),e("code",[this._v("cargo run")]),this._v(" 指定工作空间中我们希望使用的包:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo run -p adder\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running `target/debug/adder`\nHello, world! 10 plus one is 11!\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("这会运行 "),e("em",[this._v("adder/src/main.rs")]),this._v(" 中的代码,其依赖 "),e("code",[this._v("add-one")]),this._v(" crate")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"在工作空间中依赖外部-crate"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#在工作空间中依赖外部-crate","aria-hidden":"true"}},[this._v("#")]),this._v(" 在工作空间中依赖外部 crate")])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("还需注意的是工作空间只在根目录有一个 "),a("em",[t._v("Cargo.lock")]),t._v(",而不是在每一个 crate 目录都有 "),a("em",[t._v("Cargo.lock")]),t._v("。这确保了所有的 crate 都使用完全相同版本的依赖。如果在 "),a("em",[t._v("Cargo.toml")]),t._v(" 和 "),a("em",[t._v("add-one/Cargo.toml")]),t._v(" 中都增加 "),a("code",[t._v("rand")]),t._v(" crate,则 Cargo 会将其都解析为同一版本并记录到唯一的 "),a("em",[t._v("Cargo.lock")]),t._v(" 中。使得工作空间中的所有 crate 都使用相同的依赖意味着其中的 crate 都是相互兼容的。让我们在 "),a("em",[t._v("add-one/Cargo.toml")]),t._v(" 中的 "),a("code",[t._v("[dependencies]")]),t._v(" 部分增加 "),a("code",[t._v("rand")]),t._v(" crate 以便能够在 "),a("code",[t._v("add-one")]),t._v(" crate 中使用 "),a("code",[t._v("rand")]),t._v(" crate:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: add-one/Cargo.toml")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-toml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[dependencies]\n\nrand = "0.3.14"\n')])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("现在就可以在 "),a("em",[t._v("add-one/src/lib.rs")]),t._v(" 中增加 "),a("code",[t._v("extern crate rand;")]),t._v(" 了,接着在 "),a("em",[t._v("add")]),t._v(" 目录运行 "),a("code",[t._v("cargo build")]),t._v(" 构建整个工作空间就会引入并编译 "),a("code",[t._v("rand")]),t._v(" crate:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo build\n Updating registry `https://github.com/rust-lang/crates.io-index`\n Downloading rand v0.3.14\n --snip--\n Compiling rand v0.3.14\n Compiling add-one v0.1.0 (file:///projects/add/add-one)\n Compiling adder v0.1.0 (file:///projects/add/adder)\n Finished dev [unoptimized + debuginfo] target(s) in 10.18 secs\n")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("现在顶级的 "),a("em",[t._v("Cargo.lock")]),t._v(" 包含了 "),a("code",[t._v("add-one")]),t._v(" 的 "),a("code",[t._v("rand")]),t._v(" 依赖的信息。然而,即使 "),a("code",[t._v("rand")]),t._v(" 被用于工作空间的某处,也不能在其他 crate 中使用它,除非也在他们的 "),a("em",[t._v("Cargo.toml")]),t._v(" 中加入 "),a("code",[t._v("rand")]),t._v("。例如,如果在顶级的 "),a("code",[t._v("adder")]),t._v(" crate 的 "),a("em",[t._v("adder/src/main.rs")]),t._v(" 中增加 "),a("code",[t._v("extern crate rand;")]),t._v(",会得到一个错误:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo build\n Compiling adder v0.1.0 (file:///projects/add/adder)\nerror: use of unstable library feature 'rand': use `rand` from crates.io (see\nissue #27703)\n --\x3e adder/src/main.rs:1:1\n |\n1 | extern crate rand;\n")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("为了修复这个错误,修改顶级 "),a("code",[t._v("adder")]),t._v(" crate 的 "),a("em",[t._v("Cargo.toml")]),t._v(" 来表明 "),a("code",[t._v("rand")]),t._v(" 也是这个 crate 的依赖。构建 "),a("code",[t._v("adder")]),t._v(" crate 会将 "),a("code",[t._v("rand")]),t._v(" 加入到 "),a("em",[t._v("Cargo.lock")]),t._v(" 中 "),a("code",[t._v("adder")]),t._v(" 的依赖列表中,但是这并不会下载 "),a("code",[t._v("rand")]),t._v(" 的额外拷贝。Cargo 确保了工作空间中任何使用 "),a("code",[t._v("rand")]),t._v(" 的 crate 都采用相同的版本。在整个工作空间中使用相同版本的 "),a("code",[t._v("rand")]),t._v(" 节省了空间,因为这样就无需多个拷贝并确保了工作空间中的 crate 将是相互兼容的。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"为工作空间增加测试"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#为工作空间增加测试","aria-hidden":"true"}},[this._v("#")]),this._v(" 为工作空间增加测试")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("作为另一个提升,让我们为 "),e("code",[this._v("add_one")]),this._v(" crate 中的 "),e("code",[this._v("add_one::add_one")]),this._v(" 函数增加一个测试:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("span",{staticClass:"filename"},[this._v("文件名: add-one/src/lib.rs")])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"language-rust extra-class"},[a("pre",{pre:!0,attrs:{class:"language-rust"}},[a("code",[a("span",{attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("add_one")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i32"),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" i32 "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x "),a("span",{attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{attrs:{class:"token number"}},[t._v("1")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[cfg(test)]")]),t._v("\n"),a("span",{attrs:{class:"token keyword"}},[t._v("mod")]),t._v(" tests "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("use")]),t._v(" "),a("span",{attrs:{class:"token keyword"}},[t._v("super")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{attrs:{class:"token operator"}},[t._v("*")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{attrs:{class:"token attribute attr-name"}},[t._v("#[test]")]),t._v("\n "),a("span",{attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("it_works")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{attrs:{class:"token function"}},[t._v("assert_eq!")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("3")]),a("span",{attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{attrs:{class:"token function"}},[t._v("add_one")]),a("span",{attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{attrs:{class:"token number"}},[t._v("2")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("在顶级 "),e("em",[this._v("add")]),this._v(" 目录运行 "),e("code",[this._v("cargo test")]),this._v(":")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo test\n Compiling add-one v0.1.0 (file:///projects/add/add-one)\n Compiling adder v0.1.0 (file:///projects/add/adder)\n Finished dev [unoptimized + debuginfo] target(s) in 0.27 secs\n Running target/debug/deps/add_one-f0253159197f7841\n\nrunning 1 test\ntest tests::it_works ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n Running target/debug/deps/adder-f88af9d2cc175a5e\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n Doc-tests add-one\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("输出的第一部分显示 "),a("code",[t._v("add-one")]),t._v(" crate 的 "),a("code",[t._v("it_works")]),t._v(" 测试通过了。下一个部分显示 "),a("code",[t._v("adder")]),t._v(" crate 中找到了 0 个测试,最后一部分显示 "),a("code",[t._v("add-one")]),t._v(" crate 中有 0 个文档测试。在像这样的工作空间结构中运行 "),a("code",[t._v("cargo test")]),t._v(" 会运行工作空间中所有 crate 的测试。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("也可以选择运行工作空间中特定 crate 的测试,通过在根目录使用 "),e("code",[this._v("-p")]),this._v(" 参数并指定希望测试的 crate 名称:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo test -p add-one\n Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n Running target/debug/deps/add_one-b3235fea9a156f74\n\nrunning 1 test\ntest tests::it_works ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n Doc-tests add-one\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("输出显示了 "),e("code",[this._v("cargo test")]),this._v(" 只运行了 "),e("code",[this._v("add-one")]),this._v(" crate 的测试而没有运行 "),e("code",[this._v("adder")]),this._v(" crate 的测试。")])},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("p",[t._v("如果你选择向 "),a("em",[t._v("https://crates.io/")]),t._v(" 发布工作空间中的 crate,每一个工作空间中的 crate 将会单独发布。"),a("code",[t._v("cargo publish")]),t._v(" 命令并没有 "),a("code",[t._v("--all")]),t._v(" 或者 "),a("code",[t._v("-p")]),t._v(" 参数,所以必须进入每一个 crate 的目录并运行 "),a("code",[t._v("cargo publish")]),t._v(" 来发布工作空间中的每一个 crate。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("现在尝试以类似 "),e("code",[this._v("add-one")]),this._v(" crate 的方式向工作空间增加 "),e("code",[this._v("add-two")]),this._v(" crate 来作为更多的练习!")])}],!1,null,null,null);n.options.__file="ch14-03-cargo-workspaces.md";e.default=n.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/75.12d311a9.js b/src/.vuepress/dist/assets/js/75.12d311a9.js
new file mode 100644
index 0000000..070a977
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/75.12d311a9.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[75],{195:function(t,e,r){"use strict";r.r(e);var i=r(0),s=Object(i.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),r("blockquote",[r("p",[r("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch14-04-installing-binaries.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch14-04-installing-binaries.md"),r("OutboundLink")],1),t._v(" "),r("br"),t._v("\ncommit ff93f82ff63ade5a352d9ccc430945d4ec804cdf")])]),t._v(" "),r("p",[r("code",[t._v("cargo install")]),t._v(" 命令用于在本地安装和使用二进制 crate。它并不打算替换系统中的包;它意在作为一个方便 Rust 开发者们安装其他人已经在 "),r("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),r("OutboundLink")],1),t._v(" 上共享的工具的手段。只有拥有二进制目标文件的包能够被安装。二进制目标文件是在 crate 有 "),r("em",[t._v("src/main.rs")]),t._v(" 或者其他指定为二进制文件时所创建的可执行程序,这不同于自身不能执行但适合包含在其他程序中的库目标文件。通常 crate 的 "),r("em",[t._v("README")]),t._v(" 文件中有该 crate 是库、二进制目标还是两者都是的信息。")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"使用-cargo-install-从-crates-io-安装二进制文件"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#使用-cargo-install-从-crates-io-安装二进制文件","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),e("code",[this._v("cargo install")]),this._v(" 从 Crates.io 安装二进制文件")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("p",[t._v("所有来自 "),r("code",[t._v("cargo install")]),t._v(" 的二进制文件都安装到 Rust 安装根目录的 "),r("em",[t._v("bin")]),t._v(" 文件夹中。如果你使用 "),r("em",[t._v("rustup.rs")]),t._v(" 安装的 Rust 且没有自定义任何配置,这将是 "),r("code",[t._v("$HOME/.cargo/bin")]),t._v("。确保将这个目录添加到 "),r("code",[t._v("$PATH")]),t._v(" 环境变量中就能够运行通过 "),r("code",[t._v("cargo install")]),t._v(" 安装的程序了。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("例如,第十二章提到的叫做 "),e("code",[this._v("ripgrep")]),this._v(" 的用于搜索文件的 "),e("code",[this._v("grep")]),this._v(" 的 Rust 实现。如果想要安装 "),e("code",[this._v("ripgrep")]),this._v(",可以运行如下:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("$ cargo install ripgrep\nUpdating registry `https://github.com/rust-lang/crates.io-index`\n Downloading ripgrep v0.3.2\n --snip--\n Compiling ripgrep v0.3.2\n Finished release [optimized + debuginfo] target(s) in 97.91 secs\n Installing ~/.cargo/bin/rg\n")])])])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("p",[t._v("最后一行输出展示了安装的二进制文件的位置和名称,在这里 "),r("code",[t._v("ripgrep")]),t._v(" 被命名为 "),r("code",[t._v("rg")]),t._v("。只要你像上面提到的那样将安装目录加入 "),r("code",[t._v("$PATH")]),t._v(",就可以运行 "),r("code",[t._v("rg --help")]),t._v(" 并开始使用一个更快更 Rust 的工具来搜索文件了!")])}],!1,null,null,null);s.options.__file="ch14-04-installing-binaries.md";e.default=s.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/76.27ceea65.js b/src/.vuepress/dist/assets/js/76.27ceea65.js
new file mode 100644
index 0000000..3ce574f
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/76.27ceea65.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[76],{194:function(t,e,r){"use strict";r.r(e);var a=r(0),o=Object(a.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),r("blockquote",[r("p",[r("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch14-05-extending-cargo.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch14-05-extending-cargo.md"),r("OutboundLink")],1),t._v(" "),r("br"),t._v("\ncommit ff93f82ff63ade5a352d9ccc430945d4ec804cdf")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),r("p",[t._v("通过 Cargo 和 "),r("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),r("OutboundLink")],1),t._v(" 来分享代码是使得 Rust 生态环境可以用于许多不同的任务的重要组成部分。Rust 的标准库是小而稳定的,不过 crate 易于分享和使用,并采用一个不同语言自身的时间线来提供改进。不要羞于在 "),r("a",{attrs:{href:"https://crates.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("crates.io"),r("OutboundLink")],1),t._v(" 上共享对你有用的代码;因为它很有可能对别人也很有用!")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"cargo-自定义扩展命令"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#cargo-自定义扩展命令","aria-hidden":"true"}},[this._v("#")]),this._v(" Cargo 自定义扩展命令")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("p",[t._v("Cargo 被设计为可以通过新的子命令而无须修改 Cargo 自身来进行扩展。如果 "),r("code",[t._v("$PATH")]),t._v(" 中有类似 "),r("code",[t._v("cargo-something")]),t._v(" 的二进制文件,就可以通过 "),r("code",[t._v("cargo something")]),t._v(" 来像 Cargo 子命令一样运行它。像这样的自定义命令也可以运行 "),r("code",[t._v("cargo --list")]),t._v(" 来展示出来。能够通过 "),r("code",[t._v("cargo install")]),t._v(" 向 Cargo 安装扩展并可以如内建 Cargo 工具那样运行他们是 Cargo 设计上的一个非常方便的优点!")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"总结"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#总结","aria-hidden":"true"}},[this._v("#")]),this._v(" 总结")])}],!1,null,null,null);o.options.__file="ch14-05-extending-cargo.md";e.default=o.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/77.2d04e2fc.js b/src/.vuepress/dist/assets/js/77.2d04e2fc.js
new file mode 100644
index 0000000..2356c73
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/77.2d04e2fc.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[77],{193:function(t,e,_){"use strict";_.r(e);var r=_(0),v=Object(r.a)({},function(){var t=this,e=t.$createElement,_=t._self._c||e;return _("div",{staticClass:"content"},[t._m(0),t._v(" "),_("blockquote",[_("p",[_("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch15-00-smart-pointers.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch15-00-smart-pointers.md"),_("OutboundLink")],1),t._v(" "),_("br"),t._v("\ncommit 68267b982a226fa252e9afa1a5029396ccf5fa03")])]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),_("p",[t._v("考虑到智能指针是一个在 Rust 经常被使用的通用设计模式,本章并不会覆盖所有现存的智能指针。很多库都有自己的智能指针而你也可以编写属于你自己的智能指针。这里将会讲到的是来自标准库中最常用的一些:")]),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),_("p",[t._v("让我们开始吧!")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"智能指针"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#智能指针","aria-hidden":"true"}},[this._v("#")]),this._v(" 智能指针")])},function(){var t=this,e=t.$createElement,_=t._self._c||e;return _("p",[_("strong",[t._v("指针")]),t._v(" ("),_("em",[t._v("pointer")]),t._v(")是一个包含内存地址的变量的通用概念。这个地址引用,或 “指向”(points at)一些其他数据。Rust 中最常见的指针是第四章介绍的 "),_("strong",[t._v("引用")]),t._v("("),_("em",[t._v("reference")]),t._v(")。引用以 "),_("code",[t._v("&")]),t._v(" 符号为标志并借用了他们所指向的值。除了引用数据它们没有任何其他特殊功能。它们也没有任何额外开销,所以应用的最多。")])},function(){var t=this,e=t.$createElement,_=t._self._c||e;return _("p",[t._v("另一方面,"),_("strong",[t._v("智能指针")]),t._v("("),_("em",[t._v("smart pointers")]),t._v(")是一类数据结构,他们的表现类似指针,但是也拥有额外的元数据和功能。智能指针的概念并不为 Rust 所独有;其起源于 C++ 并存在于其他语言中。Rust 标准库中不同的智能指针提供了多于引用的额外功能。本章将会探索的一个例子便是 "),_("strong",[t._v("引用计数")]),t._v(" ("),_("em",[t._v("reference counting")]),t._v(")智能指针类型,其允许数据有多个所有者。引用计数智能指针记录总共有多少个所有者,并当没有任何所有者时负责清理数据。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("在 Rust 中,普通引用和智能指针的一个额外的区别是引用是一类只借用数据的指针;相反大部分情况,智能指针 "),e("strong",[this._v("拥有")]),this._v(" 他们指向的数据。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("实际上本书中已经出现过一些智能指针,比如第八章的 "),e("code",[this._v("String")]),this._v(" 和 "),e("code",[this._v("Vec")]),this._v(",虽然当时我们并不这么称呼它们。这些类型都属于智能指针因为它们拥有一些数据并允许你修改它们。它们也带有元数据(比如他们的容量)和额外的功能或保证("),e("code",[this._v("String")]),this._v(" 的数据总是有效的 UTF-8 编码)。")])},function(){var t=this,e=t.$createElement,_=t._self._c||e;return _("p",[t._v("智能指针通常使用结构体实现。智能指针区别于常规结构体的显著特性在于其实现了 "),_("code",[t._v("Deref")]),t._v(" 和 "),_("code",[t._v("Drop")]),t._v(" trait。"),_("code",[t._v("Deref")]),t._v(" trait 允许智能指针结构体实例表现的像引用一样,这样就可以编写既用于引用又用于智能指针的代码。"),_("code",[t._v("Drop")]),t._v(" trait 允许我们自定义当智能指针离开作用域时运行的代码。本章会讨论这些 trait 以及为什么对于智能指针来说他们很重要。")])},function(){var t=this,e=t.$createElement,_=t._self._c||e;return _("ul",[_("li",[_("code",[t._v("Box")]),t._v(",用于在堆上分配值")]),t._v(" "),_("li",[_("code",[t._v("Rc")]),t._v(",一个引用计数类型,其数据可以有多个所有者")]),t._v(" "),_("li",[_("code",[t._v("Ref")]),t._v(" 和 "),_("code",[t._v("RefMut")]),t._v(",通过 "),_("code",[t._v("RefCell")]),t._v(" 访问,一个在运行时而不是在编译时执行借用规则的类型。")])])},function(){var t=this,e=t.$createElement,_=t._self._c||e;return _("p",[t._v("同时我们会涉及 "),_("strong",[t._v("内部可变性")]),t._v("("),_("em",[t._v("interior mutability")]),t._v(")模式,这时不可变类型暴露出改变其内部值的 API。我们也会讨论 "),_("strong",[t._v("引用循环")]),t._v("("),_("em",[t._v("reference cycles")]),t._v(")会如何泄露内存,以及如何避免。")])}],!1,null,null,null);v.options.__file="ch15-00-smart-pointers.md";e.default=v.exports}}]);
\ No newline at end of file
diff --git a/src/.vuepress/dist/assets/js/78.cd7b5394.js b/src/.vuepress/dist/assets/js/78.cd7b5394.js
new file mode 100644
index 0000000..247bfbc
--- /dev/null
+++ b/src/.vuepress/dist/assets/js/78.cd7b5394.js
@@ -0,0 +1 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[78],{192:function(t,s,n){"use strict";n.r(s);var a=n(0),e=Object(a.a)({},function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("blockquote",[n("p",[n("a",{attrs:{href:"https://github.com/rust-lang/book/blob/master/second-edition/src/ch15-01-box.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("ch15-01-box.md"),n("OutboundLink")],1),t._v(" "),n("br"),t._v("\ncommit 0905e41f7387b60865e6eac744e31a7f7b46edf5")])]),t._v(" "),t._m(1),t._v(" "),n("p",[t._v("除了数据被储存在堆上而不是栈上之外,box 没有性能损失,不过也没有很多额外的功能。他们多用于如下场景:")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),n("p",[t._v("示例 15-2 包含一个 cons list 的枚举定义。注意这还不能编译因为这个类型没有已知的大小,之后我们会展示:")]),t._v(" "),t._m(18),t._v(" "),t._m(19),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._m(25),t._v(" "),t._m(26),t._v(" "),n("p",[t._v("如果尝试编译上面的代码,会得到如示例 15-4 所示的错误:")]),t._v(" "),t._m(27),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._v(" "),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),n("img",{staticClass:"center",staticStyle:{width:"50%"},attrs:{alt:"An infinite Cons list",src:"img/trpl15-01.svg"}}),t._v(" "),t._m(35),t._v(" "),t._m(36),t._v(" "),n("p",[t._v("Rust 无法计算出要为定义为递归的类型分配多少空间,所以编译器给出了示例 15-4 中的错误。这个错误也包括了有用的建议:")]),t._v(" "),t._m(37),n("p",[t._v("在建议中,“indirection” 意味着不同于直接储存一个值,我们将间接的储存一个指向值的指针。")]),t._v(" "),t._m(38),t._v(" "),t._m(39),t._v(" "),t._m(40),t._v(" "),t._m(41),t._v(" "),t._m(42),t._m(43),t._v(" "),t._m(44),t._v(" "),n("img",{staticClass:"center",attrs:{alt:"A finite Cons list",src:"img/trpl15-02.svg"}}),t._v(" "),t._m(45),t._v(" "),n("p",[t._v("box 只提供了间接存储和堆分配;他们并没有任何其他特殊的功能,比如我们将会见到的其他智能指针。他们也没有这些特殊功能带来的性能损失,所以他们可以用于像 cons list 这样间接存储是唯一所需功能的场景。我们还将在第十七章看到 box 的更多应用场景。")]),t._v(" "),t._m(46)])},[function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"box-t-在堆上存储数据,并且可确定大小"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#box-t-在堆上存储数据,并且可确定大小","aria-hidden":"true"}},[this._v("#")]),this._v(" "),s("code",[this._v("Box")]),this._v(" 在堆上存储数据,并且可确定大小")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("最简单直接的智能指针是 "),s("em",[this._v("box")]),this._v(",其类型是 "),s("code",[this._v("Box")]),this._v("。 box 允许你将一个值放在堆上而不是栈上。留在栈上的则是指向堆数据的指针。如果你想回顾一下栈与堆的区别请参考第四章。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("ul",[s("li",[this._v("当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候")]),this._v(" "),s("li",[this._v("当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候")]),this._v(" "),s("li",[this._v("当希望拥有一个值并只关心它的类型是否实现了特定 trait 而不是其具体类型的时候")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("我们将在本部分的余下内容中展示第一种应用场景。作为对另外两个情况更详细的说明:在第二种情况中,转移大量数据的所有权可能会花费很长的时间,因为数据在栈上进行了拷贝。为了改善这种情况下的性能,可以通过 box 将这些数据储存在堆上。接着,只有少量的指针数据在栈上被拷贝。第三种情况被称为 "),s("strong",[this._v("trait 对象")]),this._v("("),s("em",[this._v("trait object")]),this._v("),第十七章刚好有一整个部分专门讲解这个主题。所以这里所学的内容会在第十七章再次用上!")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h3",{attrs:{id:"使用-box-t-在堆上储存数据"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用-box-t-在堆上储存数据","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 "),s("code",[this._v("Box")]),this._v(" 在堆上储存数据")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("在开始 "),s("code",[this._v("Box")]),this._v(" 的用例之前,让我们熟悉一下语法和如何与储存在 "),s("code",[this._v("Box