mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 00:43:59 +08:00
wip update ch12-03
This commit is contained in:
parent
3edb20d062
commit
d465e2ff96
@ -1,7 +1,7 @@
|
|||||||
# Rust 程序设计语言(第二版) 简体中文版
|
# Rust 程序设计语言(第二版) 简体中文版
|
||||||
|
|
||||||
还在施工中:目前翻译到第十六章
|
还在施工中:目前翻译到第十六章,正在更新第十二章
|
||||||
|
|
||||||
目前官方进度:[第十六章](https://github.com/rust-lang/book/projects/1)(17~20 章还在编写当中)
|
目前官方进度:[第十七章](https://github.com/rust-lang/book/projects/1)(18~20 章还在编写当中)
|
||||||
|
|
||||||
GitBook 代码排版已大体解决,已不影响阅读
|
GitBook 代码排版已大体解决,已不影响阅读
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
123
docs/ch17-00-oop.html
Normal file
123
docs/ch17-00-oop.html
Normal file
File diff suppressed because one or more lines are too long
165
docs/ch17-01-what-is-oo.html
Normal file
165
docs/ch17-01-what-is-oo.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
312
docs/print.html
312
docs/print.html
File diff suppressed because one or more lines are too long
@ -1,7 +1,7 @@
|
|||||||
# Rust 程序设计语言(第二版) 简体中文版
|
# Rust 程序设计语言(第二版) 简体中文版
|
||||||
|
|
||||||
还在施工中:目前翻译到第十六章
|
还在施工中:目前翻译到第十六章,正在更新第十二章
|
||||||
|
|
||||||
目前官方进度:[第十六章](https://github.com/rust-lang/book/projects/1)(17~20 章还在编写当中)
|
目前官方进度:[第十七章](https://github.com/rust-lang/book/projects/1)(18~20 章还在编写当中)
|
||||||
|
|
||||||
GitBook 代码排版已大体解决,已不影响阅读
|
GitBook 代码排版已大体解决,已不影响阅读
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> [ch01-00-introduction.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch01-00-introduction.md)
|
> [ch01-00-introduction.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch01-00-introduction.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit 4f2dc564851dc04b271a2260c834643dfd86c724
|
> commit 62f78bb3f7c222b574ff547d0161c2533691f9b4
|
||||||
|
|
||||||
欢迎阅读“Rust 程序设计语言”,一本关于 Rust 的介绍性书籍。Rust 是一个着用于安全、速度和并发的编程语言。它的设计不仅可以使程序获得性能和对底层语言的控制,并且能够享受高级语言强大的抽象能力。这些特性使得 Rust 适合那些有类似 C 语言经验并正在寻找一个更安全的替代者的程序员,同时也适合那些来自类似 Python 语言背景,正在探索在不牺牲表现力的情况下编写更好性能代码的开发者。
|
欢迎阅读“Rust 程序设计语言”,一本关于 Rust 的介绍性书籍。Rust 是一个着用于安全、速度和并发的编程语言。它的设计不仅可以使程序获得性能和对底层语言的控制,并且能够享受高级语言强大的抽象能力。这些特性使得 Rust 适合那些有类似 C 语言经验并正在寻找一个更安全的替代者的程序员,同时也适合那些来自类似 Python 语言背景,正在探索在不牺牲表现力的情况下编写更好性能代码的开发者。
|
||||||
|
|
||||||
@ -15,9 +15,10 @@ registry site),[crates.io]!我们期待看到**你**使用 Rust 进行创
|
|||||||
|
|
||||||
## 为本书做出贡献
|
## 为本书做出贡献
|
||||||
|
|
||||||
本书是开源的。如果你发现任何错误,请不要犹豫,[在 GitHub 上][on GitHub]发起 issue 或提交 pull request。
|
本书是开源的。如果你发现任何错误,请不要犹豫,[在 GitHub 上][on GitHub]发起 issue 或提交 pull request。请查看[CONTRIBUTING.md]获取更多信息。
|
||||||
|
|
||||||
[on GitHub]: https://github.com/rust-lang/book
|
[on GitHub]: https://github.com/rust-lang/book
|
||||||
|
[CONTRIBUTING.md]: https://github.com/rust-lang/book/blob/master/CONTRIBUTING.md
|
||||||
|
|
||||||
> 译者注:这是本译本的 [GitHub 仓库][trpl-zh-cn],同样欢迎 Issue 和 PR :)
|
> 译者注:这是本译本的 [GitHub 仓库][trpl-zh-cn],同样欢迎 Issue 和 PR :)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> [ch04-02-references-and-borrowing.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch04-02-references-and-borrowing.md)
|
> [ch04-02-references-and-borrowing.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch04-02-references-and-borrowing.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56
|
> commit 5e0546f53cce14b126527d9ba6d1b8eb212b4f3d
|
||||||
|
|
||||||
在上一部分的结尾处的使用元组的代码是有问题的,我们需要将`String`返回给调用者函数这样就可以在调用`calculate_length`后仍然可以使用`String`了,因为`String`先被移动到了`calculate_length`。
|
在上一部分的结尾处的使用元组的代码是有问题的,我们需要将`String`返回给调用者函数这样就可以在调用`calculate_length`后仍然可以使用`String`了,因为`String`先被移动到了`calculate_length`。
|
||||||
|
|
||||||
@ -243,7 +243,7 @@ fn dangle() -> &String { // dangle returns a reference to a String
|
|||||||
|
|
||||||
因为`s`是在`dangle`创建的,当`dangle`的代码执行完毕后,`s`将被释放。不过我们尝试返回一个它的引用。这意味着这个引用会指向一个无效的`String`!这可不好。Rust 不会允许我们这么做的。
|
因为`s`是在`dangle`创建的,当`dangle`的代码执行完毕后,`s`将被释放。不过我们尝试返回一个它的引用。这意味着这个引用会指向一个无效的`String`!这可不好。Rust 不会允许我们这么做的。
|
||||||
|
|
||||||
正确的代码是直接返回`String`:
|
这里的解决方法是直接返回`String`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn no_dangle() -> String {
|
fn no_dangle() -> String {
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
> [ch06-02-match.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch06-02-match.md)
|
> [ch06-02-match.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch06-02-match.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56
|
> commit 64090418c23d615facfe49a8d548ad9baea6b097
|
||||||
|
|
||||||
Rust 有一个叫做`match`的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较并根据匹配的模式执行代码。模式可由字面值、变量、通配符和许多其他内容构成;第十八章会讲到所有不同种类的模式以及他们的作用。`match`的力量来源于模式的表现力以及编译器检查,它确保了所有可能的情况都得到处理。
|
Rust 有一个叫做`match`的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较并根据匹配的模式执行代码。模式可由字面值、变量、通配符和许多其他内容构成;第十八章会涉及到所有不同种类的模式以及他们的作用。`match`的力量来源于模式的表现力以及编译器检查,它确保了所有可能的情况都得到处理。
|
||||||
|
|
||||||
把`match`表达式想象成某种硬币分类器:硬币滑入有着不同大小孔洞的轨道,每一个硬币都会掉入符合它大小的孔洞。同样地,值也会检查`match`的每一个模式,并且在遇到第一个“符合”的模式时,值会进入相关联的代码块并在执行中被使用。
|
把`match`表达式想象成某种硬币分类器:硬币滑入有着不同大小孔洞的轨道,每一个硬币都会掉入符合它大小的孔洞。同样地,值也会检查`match`的每一个模式,并且在遇到第一个“符合”的模式时,值会进入相关联的代码块并在执行中被使用。
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> [ch08-01-vectors.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch08-01-vectors.md)
|
> [ch08-01-vectors.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch08-01-vectors.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit 4f2dc564851dc04b271a2260c834643dfd86c724
|
> commit 6c24544ba718bce0755bdaf03423af86280051d5
|
||||||
|
|
||||||
我们要讲到的第一个类型是`Vec<T>`,也被称为 *vector*。vector 允许我们在一个单独的数据结构中储存多于一个值,它在内存中彼此相邻的排列所有的值。vector 只能储存相同类型的值。他们在拥有一系列项的场景下非常实用,例如文件中的文本行或是购物车中商品的价格。
|
我们要讲到的第一个类型是`Vec<T>`,也被称为 *vector*。vector 允许我们在一个单独的数据结构中储存多于一个值,它在内存中彼此相邻的排列所有的值。vector 只能储存相同类型的值。他们在拥有一系列项的场景下非常实用,例如文件中的文本行或是购物车中商品的价格。
|
||||||
|
|
||||||
@ -133,7 +133,10 @@ let row = vec![
|
|||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
Rust 在编译时就必须准确的知道 vector 中类型的原因是它需要知道储存每个元素到底需要多少内存。第二个优点是可以准确的知道这个 vector 中允许什么类型。如果 Rust 允许 vector 存放任意类型,那么当对 vector 元素执行操作时一个或多个类型的值就有可能会造成错误。使用枚举外加`match`意味着 Rust 能在编译时就保证总是会处理所有可能的情况,正如第六章讲到的那样。
|
<span class="caption">Listing 8-1: Defining an enum to be able to hold
|
||||||
|
different types of data in a vector</span>
|
||||||
|
|
||||||
|
Rust 在编译时就必须准确的知道 vector 中类型的原因是它需要知道储存每个元素到底需要多少内存。第二个好处是可以准确的知道这个 vector 中允许什么类型。如果 Rust 允许 vector 存放任意类型,那么当对 vector 元素执行操作时一个或多个类型的值就有可能会造成错误。使用枚举外加`match`意味着 Rust 能在编译时就保证总是会处理所有可能的情况,正如第六章讲到的那样。
|
||||||
|
|
||||||
如果在编写程序时不能确切无遗的知道运行时会储存进 vector 的所有类型,枚举技术就行不通了。相反,你可以使用 trait 对象,第十七章会讲到它。
|
如果在编写程序时不能确切无遗的知道运行时会储存进 vector 的所有类型,枚举技术就行不通了。相反,你可以使用 trait 对象,第十七章会讲到它。
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> [ch09-01-unrecoverable-errors-with-panic.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md)
|
> [ch09-01-unrecoverable-errors-with-panic.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56
|
> commit e26bb338ab14b98a850c3464e821d54940a45672
|
||||||
|
|
||||||
突然有一天,糟糕的事情发生了,而你对此束手无策。对于这种情况,Rust 有`panic!`宏。当执行这个宏时,程序会打印出一个错误信息,展开并清理栈数据,然后接着退出。出现这种情况的场景通常是检测到一些类型的 bug 而且程序员并不清楚该如何处理它。
|
突然有一天,糟糕的事情发生了,而你对此束手无策。对于这种情况,Rust 有`panic!`宏。当执行这个宏时,程序会打印出一个错误信息,展开并清理栈数据,然后接着退出。出现这种情况的场景通常是检测到一些类型的 bug 而且程序员并不清楚该如何处理它。
|
||||||
|
|
||||||
@ -80,28 +80,40 @@ error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
|
|||||||
$ RUST_BACKTRACE=1 cargo run
|
$ RUST_BACKTRACE=1 cargo run
|
||||||
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
|
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
|
||||||
Running `target/debug/panic`
|
Running `target/debug/panic`
|
||||||
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is
|
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 100', /stable-dist-rustc/build/src/libcollections/vec.rs:1392
|
||||||
100', /stable-dist-rustc/build/src/libcollections/vec.rs:1395
|
|
||||||
stack backtrace:
|
stack backtrace:
|
||||||
1: 0x10922522c -
|
1: 0x560ed90ec04c - std::sys::imp::backtrace::tracing::imp::write::hf33ae72d0baa11ed
|
||||||
std::sys::imp::backtrace::tracing::imp::write::h1204ab053b688140
|
at /stable-dist-rustc/build/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:42
|
||||||
2: 0x10922649e -
|
2: 0x560ed90ee03e - std::panicking::default_hook::{{closure}}::h59672b733cc6a455
|
||||||
std::panicking::default_hook::{{closure}}::h1204ab053b688140
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:351
|
||||||
3: 0x109226140 - std::panicking::default_hook::h1204ab053b688140
|
3: 0x560ed90edc44 - std::panicking::default_hook::h1670459d2f3f8843
|
||||||
4: 0x109226897 -
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:367
|
||||||
std::panicking::rust_panic_with_hook::h1204ab053b688140
|
4: 0x560ed90ee41b - std::panicking::rust_panic_with_hook::hcf0ddb069e7abcd7
|
||||||
5: 0x1092266f4 - std::panicking::begin_panic::h1204ab053b688140
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:555
|
||||||
6: 0x109226662 - std::panicking::begin_panic_fmt::h1204ab053b688140
|
5: 0x560ed90ee2b4 - std::panicking::begin_panic::hd6eb68e27bdf6140
|
||||||
7: 0x1092265c7 - rust_begin_unwind
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:517
|
||||||
8: 0x1092486f0 - core::panicking::panic_fmt::h1204ab053b688140
|
6: 0x560ed90ee1d9 - std::panicking::begin_panic_fmt::abcd5965948b877f8
|
||||||
9: 0x109248668 -
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:501
|
||||||
core::panicking::panic_bounds_check::h1204ab053b688140
|
7: 0x560ed90ee167 - rust_begin_unwind
|
||||||
10: 0x1092205b5 - <collections::vec::Vec<T> as
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:477
|
||||||
core::ops::Index<usize>>::index::h1204ab053b688140
|
8: 0x560ed911401d - core::panicking::panic_fmt::hc0f6d7b2c300cdd9
|
||||||
11: 0x10922066a - panic::main::h1204ab053b688140
|
at /stable-dist-rustc/build/src/libcore/panicking.rs:69
|
||||||
12: 0x1092282ba - __rust_maybe_catch_panic
|
9: 0x560ed9113fc8 - core::panicking::panic_bounds_check::h02a4af86d01b3e96
|
||||||
13: 0x109226b16 - std::rt::lang_start::h1204ab053b688140
|
at /stable-dist-rustc/build/src/libcore/panicking.rs:56
|
||||||
14: 0x1092206e9 - main
|
10: 0x560ed90e71c5 - <collections::vec::Vec<T> as core::ops::Index<usize>>::index::h98abcd4e2a74c41
|
||||||
|
at /stable-dist-rustc/build/src/libcollections/vec.rs:1392
|
||||||
|
11: 0x560ed90e727a - panic::main::h5d6b77c20526bc35
|
||||||
|
at /home/you/projects/panic/src/main.rs:4
|
||||||
|
12: 0x560ed90f5d6a - __rust_maybe_catch_panic
|
||||||
|
at /stable-dist-rustc/build/src/libpanic_unwind/lib.rs:98
|
||||||
|
13: 0x560ed90ee926 - std::rt::lang_start::hd7c880a37a646e81
|
||||||
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:436
|
||||||
|
at /stable-dist-rustc/build/src/libstd/panic.rs:361
|
||||||
|
at /stable-dist-rustc/build/src/libstd/rt.rs:57
|
||||||
|
14: 0x560ed90e7302 - main
|
||||||
|
15: 0x7f0d53f16400 - __libc_start_main
|
||||||
|
16: 0x560ed90e6659 - _start
|
||||||
|
17: 0x0 - <unknown>
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">Listing 9-1: The backtrace generated by a call to
|
<span class="caption">Listing 9-1: The backtrace generated by a call to
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> [ch10-02-traits.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch10-02-traits.md)
|
> [ch10-02-traits.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch10-02-traits.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56
|
> commit e5a987f5da3fba24e55f5c7102ec63f9dc3bc360
|
||||||
|
|
||||||
trait 允许我们进行另一种抽象:他们让我们可以抽象类型所通用的行为。*trait* 告诉 Rust 编译器某个特定类型拥有可能与其他类型共享的功能。在使用泛型类型参数的场景中,可以使用 *trait bounds* 在编译时指定泛型可以是任何实现了某个 trait 的类型,并由此在这个场景下拥有我们希望的功能。
|
trait 允许我们进行另一种抽象:他们让我们可以抽象类型所通用的行为。*trait* 告诉 Rust 编译器某个特定类型拥有可能与其他类型共享的功能。在使用泛型类型参数的场景中,可以使用 *trait bounds* 在编译时指定泛型可以是任何实现了某个 trait 的类型,并由此在这个场景下拥有我们希望的功能。
|
||||||
|
|
||||||
|
@ -1,21 +1,35 @@
|
|||||||
# 一个 I/O 项目
|
# 一个 I/O 项目:构建一个小巧的 grep
|
||||||
|
|
||||||
> [ch12-00-an-io-project.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch12-00-an-io-project.md)
|
> [ch12-00-an-io-project.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch12-00-an-io-project.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit 4f2dc564851dc04b271a2260c834643dfd86c724
|
> commit 1f432fc231cfbc310433ab2a354d77058444288c
|
||||||
|
|
||||||
之前几个章节我们学习了很多知识。让我们一起运用这些新知识来构建一个项目。在这个过程中,我们还将学习到更多 Rust 标准库的内容。
|
<!-- We might need a more descriptive title, something that captures the new
|
||||||
|
elements we're introducing -- are we going to cover things like environment
|
||||||
|
variables more in later chapters, or is this the only place we explain how to
|
||||||
|
use them? -->
|
||||||
|
|
||||||
那么我们应该写点什么呢?这得是一个利用 Rust 优势的项目。Rust 的一个强大的用途是命令行工具:Rust 的运行速度、安全性、“单二进制文件”输出和跨平台支持使得它称为这类工作的绝佳选择。所以我们将创建一个我们自己的经典命令行工具:`grep`。`grep`有着极为简单的应用场景,它完成如下工作:
|
<!-- This is the only place we were planning on explaining both environment
|
||||||
|
variables and printing to standard error. These are things that people commonly
|
||||||
|
want to know how to do in Rust, but there's not much more than what we've said
|
||||||
|
here about them, people just want to know how to do them in Rust. We realize
|
||||||
|
that those sections make this chapter long, but think it's worth it to include
|
||||||
|
information that people want. We've gotten really positive feedback from people
|
||||||
|
who have read this chapter online; people who like learning through projects
|
||||||
|
have really enjoyed this chapter. /Carol-->
|
||||||
|
|
||||||
1. 它获取一个文件和一个字符串作为参数。
|
本章既是一个目前所学的很多技能的概括,也是一个更多标准库功能的探索。我们将构建一个与文件和命令行输入/输出交互的命令行工具来练习现在一些你已经掌握的 Rust 技能。
|
||||||
2. 读取文件
|
|
||||||
3. 寻找文件中包含字符串参数的行
|
|
||||||
4. 打印出这些行
|
|
||||||
|
|
||||||
另外,我们还将添加一个额外的功能:一个环境变量允许我们大小写不敏感的搜索字符串参数。
|
Rust 的运行速度、安全性、“单二进制文件”输出和跨平台支持使其成为创建命令行程序的绝佳选择,所以我们的项目将创建一个我们自己版本的经典命令行工具:`grep`。grep 是“Globally search a Regular Expression and Print.”的首字母缩写。`grep`最简单的使用场景是使用如下步骤在特定文件中搜索指定字符串:
|
||||||
|
|
||||||
还有另一个很好的理由使用`grep`作为示例项目:Rust 社区的成员,Andrew Gallant,已经使用 Rust 创建了一个功能非常完整的`grep`版本。它叫做`ripgrep`,并且它非常非常快。这样虽然我们的`grep`将会非常简单,你也会掌握阅读现真实项目的基础知识。
|
- 获取一个文件和一个字符串作为参数。
|
||||||
|
- 读取文件
|
||||||
|
- 寻找文件中包含字符串参数的行
|
||||||
|
- 打印出这些行
|
||||||
|
|
||||||
|
我们还会展示如何使用环境变量和打印到标准错误而不是标准输出;这些功能在命令行工具中是很常用的。
|
||||||
|
|
||||||
|
一位 Rust 社区的成员,Andrew Gallant,已经创建了一个功能完整且非常快速的`grep`版本,叫做`ripgrep`。相比之下,我们的`grep`将非常简单,本章将交给你一些帮助你理解像`ripgrep`这样真实项目的背景知识。
|
||||||
|
|
||||||
这个项目将会结合之前所学的一些内容:
|
这个项目将会结合之前所学的一些内容:
|
||||||
|
|
||||||
@ -25,14 +39,12 @@
|
|||||||
- 合理的使用 trait 和生命周期(第十章)
|
- 合理的使用 trait 和生命周期(第十章)
|
||||||
- 测试(第十一章)
|
- 测试(第十一章)
|
||||||
|
|
||||||
另外,我还会简要的讲到闭包、迭代器和 trait 对象,他们分别会在第XX、YY和ZZ章详细介绍。
|
另外,我还会简要的讲到闭包、迭代器和 trait 对象,他们分别会在第十三章和第十七章中详细介绍。
|
||||||
|
|
||||||
让我们一如既往的使用`cargo new`创建一个新项目:
|
让我们一如既往的使用`cargo new`创建一个新项目。我们称之为`greprs`以便与可能已经安装在系统上的`grep`工具相区别:
|
||||||
|
|
||||||
```text
|
```
|
||||||
$ cargo new --bin greprs
|
$ cargo new --bin greprs
|
||||||
Created binary (application) `greprs` project
|
Created binary (application) `greprs` project
|
||||||
$ cd greprs
|
$ cd greprs
|
||||||
```
|
```
|
||||||
|
|
||||||
我们版本的`grep`的叫做“greprs”,这样就不会迷惑用户让他们以为这就是可能已经在系统上安装了功能更完整的`grep`。
|
|
@ -2,16 +2,38 @@
|
|||||||
|
|
||||||
> [ch12-01-accepting-command-line-arguments.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch12-01-accepting-command-line-arguments.md)
|
> [ch12-01-accepting-command-line-arguments.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch12-01-accepting-command-line-arguments.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit c49e5ee8859f8eb8f8867cbeafbdf5b802aa5894
|
> commit b8e4fcbf289b82c12121b282747ce05180afb1fb
|
||||||
|
|
||||||
第一个任务是让`greprs`接受两个命令行参数。crates.io 上有一些现存的库可以帮助我们,不过因为我们正在学习,我们将自己实现一个。
|
第一个任务是让`greprs`能够接受两个命令行参数:文件名和要搜索的字符串。也就是说希望能够使用`cargo run`,要搜索的字符串和被搜索的文件的路径来运行程序,像这样:
|
||||||
|
|
||||||
我们需要调用一个 Rust 标准库提供的函数:`std::env::args`。这个函数返回一个传递给程序的命令行参数的**迭代器**(*iterator*)。我们还未讨论到迭代器,第十三章会全面的介绍他们。但是对于我们的目的来说,使用他们并不需要知道多少技术细节。我们只需要明白两点:
|
```
|
||||||
|
$ cargo run searchstring example-filename.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
现在`cargo new`生成的程序忽略任何传递给它的参数。crates.io 上有一些现存的可以帮助我们接受命令行参数的库,不过因为我们正在学习,让我们实现一个。
|
||||||
|
|
||||||
|
<!--Below -- I'm not clear what we need the args function for, yet, can you set
|
||||||
|
it out more concretely? Otherwise, will it make more sense in context of the
|
||||||
|
code later? Is this function needed to allow our function to accept arguments,
|
||||||
|
is that was "args" is for? -->
|
||||||
|
<!-- We mentioned in the intro to this chapter that grep takes as arguments a
|
||||||
|
filename and a string. I've added an example of how we want to run our
|
||||||
|
resulting tool and what we want the behavior to be, please let me know if this
|
||||||
|
doesn't clear it up. /Carol-->
|
||||||
|
|
||||||
|
### 读取参数值
|
||||||
|
|
||||||
|
为了能够获取传递给程序的命令行参数的值,我们需要调用一个 Rust 标准库提供的函数:`std::env::args`。这个函数返回一个传递给程序的命令行参数的**迭代器**(*iterator*)。我们还未讨论到迭代器,第十三章会全面的介绍他们。但是对于我们现在的目的来说只需要明白两点:
|
||||||
|
|
||||||
1. 迭代器生成一系列的值。
|
1. 迭代器生成一系列的值。
|
||||||
2. 在迭代器上调用`collect`方法可以将其生成的元素转换为一个 vector。
|
2. 在迭代器上调用`collect`方法可以将其生成的元素转换为一个 vector。
|
||||||
|
|
||||||
让我们试试列表 12-1 中的代码:
|
让我们尝试一下:使用列表 12-1 中的代码来读取任何传递给`greprs`的命令行参数并将其收集到一个 vector 中。
|
||||||
|
|
||||||
|
<!-- Give what a try, here, what are we making? Can you lay that out? I've
|
||||||
|
tried above but I'm not sure it's complete -->
|
||||||
|
<!-- We're not creating anything, we're just reading. I'm not sure if I've made
|
||||||
|
this clearer. /Carol -->
|
||||||
|
|
||||||
<span class="filename">Filename: src/main.rs</span>
|
<span class="filename">Filename: src/main.rs</span>
|
||||||
|
|
||||||
@ -24,14 +46,26 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">Listing 12-1: Collect the command line arguments into a
|
Listing 12-1: Collect the command line arguments into a vector and print them
|
||||||
vector and print them out</span>
|
out
|
||||||
|
|
||||||
<!-- Will add wingdings in libreoffice /Carol -->
|
<!-- Will add wingdings in libreoffice /Carol -->
|
||||||
|
|
||||||
首先使用`use`语句来将`std::env`模块引入作用域。当函数嵌套了多于一层模块时,比如说`std::env::args`,通常使用`use`将父模块引入作用域,而不是引入其本身。`env::args`比单独的`args`要明确一些。当然,如果使用了多于一个`std::env`中的函数时,我们也只需要一个`use`语句。
|
首先使用`use`语句来将`std::env`模块引入作用域以便可以使用它的`args`函数。注意`std::env::args`函数嵌套进了两层模块中。如第七章讲到的,当所需函数嵌套了多于一层模块时,通常将父模块引入作用域,而不是其自身。这便于我们利用`std::env`中的其他函数。这比增加了`use std::env::args;`后仅仅使用`args`调用函数要更明确一些;这样看起来好像一个定义于当前模块的函数。
|
||||||
|
|
||||||
在`main`函数的第一行,我们调用了`env::args`,并立即使用`collect`来创建了一个 vector。这里我们也显式的注明了`args`的类型:`collect`可以被用来创建很多类型的集合。Rust 并不能推断出我们需要什么类型,所以类型注解是必须的。在 Rust 中我们很少会需要注明类型,不过`collect`是就一个通常需要这么做的函数。
|
<!-- We realized that we need to add the following caveat to fully specify
|
||||||
|
the behavior of `std::env::args` /Carol -->
|
||||||
|
|
||||||
|
<!-- PROD: START BOX -->
|
||||||
|
|
||||||
|
> 注意:`std::env::args`在其任何参数包含无效 Unicode 字符时会 panic。如果你需要接受包含无效 Unicode 字符的参数,使用`std::env::args_os`代替。这个函数返回`OsString`值而不是`String`值。出于简单考虑这里使用`std::env::args`,因为`OsString`值每个平台都不一样而且比`String`值处理起来更复杂。
|
||||||
|
|
||||||
|
<!-- PROD: END BOX -->
|
||||||
|
|
||||||
|
<!--what is it we're making into a vector here, the arguments we pass?-->
|
||||||
|
<!-- The iterator of the arguments. /Carol -->
|
||||||
|
|
||||||
|
在`main`函数的第一行,我们调用了`env::args`,并立即使用`collect`来创建了一个包含迭代器所有值的 vector。`collect`可以被用来创建很多类型的集合,所以这里显式注明的`args`类型来指定我们需要一个字符串 vector。虽然在 Rust 中我们很少会需要注明类型,`collect`就是一个经常需要注明类型的函数,因为 Rust 不能推断出你想要什么类型的集合。
|
||||||
|
|
||||||
最后,我们使用调试格式`:?`打印出 vector。让我们尝试不用参数运行代码,接着用两个参数:
|
最后,我们使用调试格式`:?`打印出 vector。让我们尝试不用参数运行代码,接着用两个参数:
|
||||||
|
|
||||||
@ -44,50 +78,58 @@ $ cargo run needle haystack
|
|||||||
["target/debug/greprs", "needle", "haystack"]
|
["target/debug/greprs", "needle", "haystack"]
|
||||||
```
|
```
|
||||||
|
|
||||||
你会注意一个有趣的事情:二进制文件的名字是第一个参数。其原因超出了本章介绍的范围,不过这是我们必须记住的。
|
<!--Below --- This initially confused me, do you mean that the argument at
|
||||||
|
index 0 is taken up by the name of the binary, so we start arguments at 1 when
|
||||||
|
setting them? It seems like it's something like that, reading on, and I've
|
||||||
|
edited as such, can you check? -->
|
||||||
|
<!-- Mentioning the indexes here seemed repetitive with the text after Listing
|
||||||
|
12-2. We're not "setting" arguments here, we're saving the value in variables.
|
||||||
|
I've hopefully cleared this up without needing to introduce repetition.
|
||||||
|
/Carol-->
|
||||||
|
|
||||||
现在我们有了一个访问所有参数的方法,让我们如列表 12-2 中所示将需要的变量存放到变量中:
|
你可能注意到了 vector 的第一个值是"target/debug/greprs",它是二进制我呢见的名称。其原因超出了本章介绍的范围,不过需要记住的是我们保存了所需的两个参数。
|
||||||
|
|
||||||
|
### 将参数值保存进变量
|
||||||
|
|
||||||
|
打印出参数 vector 中的值仅仅展示了可以访问程序中指定为命令行参数的值。但是这并不是我们想要做的,我们希望将这两个参数的值保存进变量这样就可以在程序使用这些值。让我们如列表 12-2 这样做:
|
||||||
|
|
||||||
|
<!-- By 'find the ones we care about' did you mean set particular arguments so
|
||||||
|
the user knows what to enter? I'm a little confused about what we are doing,
|
||||||
|
I've tried to clarify above -->
|
||||||
|
<!-- We're incrementally adding features and adding some code that helps the
|
||||||
|
reader be able to see and experience what the code is doing rather than just
|
||||||
|
taking our word for it. I've hopefully clarified below. /Carol -->
|
||||||
|
|
||||||
<span class="filename">Filename: src/main.rs</span>
|
<span class="filename">Filename: src/main.rs</span>
|
||||||
|
|
||||||
```rust,ignore
|
```rust,should_panic
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
let search = &args[1];
|
let query = &args[1];
|
||||||
let filename = &args[2];
|
let filename = &args[2];
|
||||||
|
|
||||||
println!("Searching for {}", search);
|
println!("Searching for {}", query);
|
||||||
println!("In file {}", filename);
|
println!("In file {}", filename);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">Listing 12-2: Create variables to hold the search
|
Listing 12-2: Create variables to hold the query argument and filename argument
|
||||||
argument and filename argument</span>
|
|
||||||
|
|
||||||
<!-- Will add ghosting and wingdings in libreoffice /Carol -->
|
<!-- Will add ghosting and wingdings in libreoffice /Carol -->
|
||||||
|
|
||||||
记住,程序名称是是第一个参数,所以并不需要`args[0]`。我们决定从第一个参数将是需要搜索的字符串,所以将第一个参数的引用放入变量`search`中。第二个参数将是文件名,将其放入变量`filename`中。再次尝试运行程序:
|
正如我们在打印出 vector 时所看到的,程序的名称占据了 vector 的第一个值`args[0]`,所以我们从索引`1`开始。第一个参数`greprs`是需要搜索的字符串,所以将其将第一个参数的引用存放在变量`query`中。第二个参数将是文件名,所以将第二个参数的引用放入变量`filename`中。
|
||||||
|
|
||||||
|
我们将临时打印出出这些变量的值,再一次证明代码如我们期望的那样工作。让我们使用参数`test`和`sample.txt`再次运行这个程序:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ cargo run test sample.txt
|
$ cargo run test sample.txt
|
||||||
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
|
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
|
||||||
Running `target\debug\greprs.exe test sample.txt`
|
Running `target/debug/greprs test sample.txt`
|
||||||
Searching for test
|
Searching for test
|
||||||
In file sample.txt
|
In file sample.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
很棒!不过有一个问题。让我们不带参数运行:
|
好的,它可以工作!我们将所需的参数值保存进了对应的变量中。之后会增加一些错误处理来应对类似用户没有提供参数的情况,不过现在我们将忽略他们并开始增加读取文件功能。
|
||||||
|
|
||||||
```
|
|
||||||
$ cargo run
|
|
||||||
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
|
|
||||||
Running `target\debug\greprs.exe`
|
|
||||||
thread 'main' panicked at 'index out of bounds: the len is 1
|
|
||||||
but the index is 1', ../src/libcollections\vec.rs:1307
|
|
||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
||||||
```
|
|
||||||
|
|
||||||
因为 vector 中只有一个元素,就是程序名称,不过我们尝试访问第二元素,程序 panic 并提示越界访问。虽然这个错误信息是_准确的_,不过它对程序的用户来说就没有意义了。现在就可以修复这个问题,不过我先继续学习别的内容:在程序结束前我们会改善这个情况。
|
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
> [ch12-02-reading-a-file.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch12-02-reading-a-file.md)
|
> [ch12-02-reading-a-file.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch12-02-reading-a-file.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit c49e5ee8859f8eb8f8867cbeafbdf5b802aa5894
|
> commit b8e4fcbf289b82c12121b282747ce05180afb1fb
|
||||||
|
|
||||||
现在有了一些包含我们需要的信息的变量了,让我们试着使用他们。下一步目标是打开需要搜索的文件。为此,我需要一个文件。在项目的根目录创建一个文件`poem.txt`,并写入一些艾米莉·狄金森(Emily Dickinson)的诗:
|
接下来我们将读取由命令行文件名参数指定的文件。首先,需要一个用来测试的示例文件——用来确保`greprs`正常工作的最好的文件是拥有少量文本和多个行且有一些重复单词的文件。列表 12-3 是一首艾米莉·狄金森(Emily Dickinson)的诗,它正适合这个工作!在项目根目录创建一个文件`poem.txt`,并输入诗 "I'm nobody! Who are you?":
|
||||||
|
|
||||||
<span class="filename">Filename: poem.txt</span>
|
<span class="filename">Filename: poem.txt</span>
|
||||||
|
|
||||||
```
|
```text
|
||||||
I'm nobody! Who are you?
|
I'm nobody! Who are you?
|
||||||
Are you nobody, too?
|
Are you nobody, too?
|
||||||
Then there's a pair of us — don't tell!
|
Then there's a pair of us — don't tell!
|
||||||
@ -20,16 +20,21 @@ To tell your name the livelong day
|
|||||||
To an admiring bog!
|
To an admiring bog!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<span class="caption">Listing 12-3: The poem "I'm nobody! Who are you?" by
|
||||||
|
Emily Dickinson that will make a good test case</span>
|
||||||
|
|
||||||
<!-- Public domain Emily Dickinson poem. This will work best with something
|
<!-- Public domain Emily Dickinson poem. This will work best with something
|
||||||
short, but that has multiple lines and some repetition. We could search through
|
short, but that has multiple lines and some repetition. We could search through
|
||||||
code; that gets a bit meta and possibly confusing... Changes to this are most
|
code; that gets a bit meta and possibly confusing... Changes to this are most
|
||||||
welcome. /Carol -->
|
welcome. /Carol -->
|
||||||
|
<!-- :D I like it! I'm all for keeping -->
|
||||||
|
<!-- Great! /Carol -->
|
||||||
|
|
||||||
创建完这个文件后,让我们编辑 *src/main.rs* 并增加如列表 12-3 所示用来打开文件的代码:
|
创建完这个文件之后,修改 *src/main.rs* 并增加如列表 12-4 所示的打开文件的代码:
|
||||||
|
|
||||||
<span class="filename">Filename: src/main.rs</span>
|
<span class="filename">Filename: src/main.rs</span>
|
||||||
|
|
||||||
```rust,ignore
|
```rust,should_panic
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
@ -37,10 +42,10 @@ use std::io::prelude::*;
|
|||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
let search = &args[1];
|
let query = &args[1];
|
||||||
let filename = &args[2];
|
let filename = &args[2];
|
||||||
|
|
||||||
println!("Searching for {}", search);
|
println!("Searching for {}", query);
|
||||||
println!("In file {}", filename);
|
println!("In file {}", filename);
|
||||||
|
|
||||||
let mut f = File::open(filename).expect("file not found");
|
let mut f = File::open(filename).expect("file not found");
|
||||||
@ -52,21 +57,22 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">Listing 12-3: Read the contents of the file specified by
|
Listing 12-4: Reading the contents of the file specified by the second argument
|
||||||
the second argument</span>
|
|
||||||
|
|
||||||
<!-- Will add ghosting and wingdings in libreoffice /Carol -->
|
<!-- Will add ghosting and wingdings in libreoffice /Carol -->
|
||||||
|
|
||||||
这里增加了一些新内容。首先,需要更多的`use`语句来引入标准库中的相关部分:我们需要`std::fs::File`来处理文件,而`std::io::prelude::*`则包含许多对于 I/O 包括文件 I/O 有帮助的 trait。类似于 Rust 有一个通用的 prelude 来自动引入特定内容,`std::io`也有其自己的 prelude 来引入处理 I/O 时需要的内容。不同于默认的 prelude,必须显式`use`位于`std::io`中的 prelude。
|
首先,增加了更多的`use`语句来引入标准库中的相关部分:需要`std::fs::File`来处理文件,而`std::io::prelude::*`则包含许多对于 I/O 包括文件 I/O 有帮助的 trait。类似于 Rust 有一个通用的 prelude 来自动引入特定内容,`std::io`也有其自己的 prelude 来引入处理 I/O 时所需的通用内容。不同于默认的 prelude,必须显式`use`位于`std::io`中的 prelude。
|
||||||
|
|
||||||
在`main`中,我们增加了三点内容:第一,我们获取了文件的句柄并使用`File::open`函数与第二个参数中指定的文件名来打开这个文件。第二,我们在变量`contents`中创建了一个空的可变的`String`,接着对文件句柄调用`read_to_string`并以`contents`字符串作为参数,`contents`是`read_to_string`将会放置它读取到的数据地方。最后,我们打印出了整个文件的内容,这是一个确认目前为止的程序能够工作的方法。
|
在`main`中,我们增加了三点内容:第一,通过传递变量`filename`的值调用`File::open`函数的值来获取文件的可变句柄。创建了叫做`contents`的变量并将其设置为一个可变的,空的`String`。它将会存放之后读取的文件的内容。第三,对文件句柄调用`read_to_string`并传递`contents`的可变引用作为参数。
|
||||||
|
|
||||||
尝试运行这些代码,随意指定第一个参数(因为还未实现搜索功能的部分)而将 *poem.txt* 文件将作为第二个参数:
|
在这些代码之后,我们再次增加了临时的`println!`打印出读取文件后`contents`的值,这样就可以检查目前为止的程序能否工作。
|
||||||
|
|
||||||
|
尝试运行这些代码,随意指定一个字符串作为第一个命令行参数(因为还未实现搜索功能的部分)而将 *poem.txt* 文件将作为第二个参数:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ cargo run the poem.txt
|
$ cargo run the poem.txt
|
||||||
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
|
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
|
||||||
Running `target\debug\greprs.exe the poem.txt`
|
Running `target/debug/greprs the poem.txt`
|
||||||
Searching for the
|
Searching for the
|
||||||
In file poem.txt
|
In file poem.txt
|
||||||
With text:
|
With text:
|
||||||
@ -81,4 +87,4 @@ To tell your name the livelong day
|
|||||||
To an admiring bog!
|
To an admiring bog!
|
||||||
```
|
```
|
||||||
|
|
||||||
好的!我们的代码可以工作了!然而,它还有一些瑕疵。因为程序还很小,这些瑕疵并不是什么大问题,不过随着程序功能的丰富,将会越来越难以用简单的方法修复他们。现在就让我们开始重构而不是等待之后处理。重构在只有少量代码时会显得容易得多。
|
好的!代码读取并打印出了文件的内容。虽然它还有一些瑕疵:`main`函数有着多个功能,同时也没有处理可能出现的错误。虽然我们的程序还很小,这些瑕疵并不是什么大问题。不过随着程序功能的丰富,将会越来越难以用简单的方法修复他们。在开发程序时,及早开始重构是一个最佳实践,因为重构少量代码时要容易的多,所以让我们现在就开始吧。
|
||||||
|
@ -2,20 +2,28 @@
|
|||||||
|
|
||||||
> [ch12-03-improving-error-handling-and-modularity.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch12-03-improving-error-handling-and-modularity.md)
|
> [ch12-03-improving-error-handling-and-modularity.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch12-03-improving-error-handling-and-modularity.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit bdab3f38da5b7bf7277bfe21ec59a7a81880e6b4
|
> commit b8e4fcbf289b82c12121b282747ce05180afb1fb
|
||||||
|
|
||||||
为了完善我们程序有四个问题需要修复,而他们都与潜在的错误和程序结构有关。第一个问题是在哪打开文件:我们使用了`expect`来在打开文件失败时指定一个错误信息,不过这个错误信息只是说“文件不存在”。还有很多打开文件失败的方式,不过我们总是假设是由于缺少文件导致的。例如,文件存在但是没有打开它的权限:这时,我们就打印出了错误不符合事实的错误信息!
|
为了改善我们的程序这里有四个问题需要修复,而且他们都与程序的组织方式和如何处理潜在错误有关。
|
||||||
|
|
||||||
第二,我们不停的使用`expect`,这就有点类似我们之前在不传递任何命令行参数时索引会`panic!`时注意到的问题:这虽然时_可以工作_的,不过这有点没有原则性,而且整个程序中都需要他们,将错误处理都置于一处则会显得好很多。
|
第一,`main`现在进行了两个任务:它解析了参数并打开了文件。对于一个这样的小函数,这并不是一个大问题。然而如果`main`中的功能持续增加,`main`函数处理的单独的任务也会增加。当函数承担了更多责任,它就更难以推导,更难以测试,并且更难以在不破坏其他部分的情况下做出修改。最好能分离出功能这样每个函数就负责一个任务。
|
||||||
|
|
||||||
第三个问题是`main`函数现在处理两个工作:解析参数,并打开文件。对于一个小的函数来说,这不是什么大问题。然而随着程序中的`main`函数不断增长,`main`函数中独立的任务也会越来越多。因为一个函数拥有很多职责,它将难以理解、难以测试并难以在不破坏其他部分的情况下做出修改。
|
这同时也关系到第二个问题:`search`和`filename`是程序中的配置变量,而像`f`和`contents`则用来执行程序逻辑。随着`main`函数的增长,就需要引入更多的变量到作用域中,而当作用域中有更多的变量时,将更难以追踪每个变量的目的。最好能将将配置变量组织进一个结构这样就能使他们的目的更明确了。
|
||||||
|
|
||||||
这也关系到我们的第四个问题:`search`和`filename`是程序中配置性的变量,而像`f`和`contents`则用来执行程序逻辑。随着`main`函数增长,将引入更多的变量到作用域中,而当作用域中有更多的变量,将更难以追踪哪个变量用于什么目的。如果能够将配置型变量组织进一个结构就能使他们的目的更明确了。
|
第三个问题是如果打开文件失败我们使用`expect`来打印出错误信息,不过这个错误信息只是说`file not found`。除了缺少文件之外还有很多打开文件可能失败的方式:例如,文件可能存在,不过可能没有打开它的权限。如果我们现在就出于这种情况,打印出的`file not found`错误信息就给了用户一个不符合事实的建议!
|
||||||
|
|
||||||
让我们重新组织程序来解决这些问题。
|
第四,我们不停的使用`expect`来处理不同的错误,如果用户没有指定足够的参数来运行程序,他们会从 Rust 得到 "index out of bounds" 错误而这并不能明确的解释问题。如果所有的错误处理都位于一处这样将来的维护者在需要修改错误处理逻辑时就只需要咨询一处代码。将所有的错误处理都放在一处也有助于确保我们打印的错误信息对终端用户来说是有意义的。
|
||||||
|
|
||||||
|
让我们通过重构项目来解决这些问题。
|
||||||
|
|
||||||
### 二进制项目的关注分离
|
### 二进制项目的关注分离
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
这类项目组织上的问题在很多相似类型的项目中很常见,所以 Rust 社区开发出一种关注分离的组织模式。这种模式可以用来组织任何用 Rust 构建的二进制项目,所以可以证明应该更早的开始这项重构,以为我们的项目符合这个模式。这个模式看起来像这样:
|
这类项目组织上的问题在很多相似类型的项目中很常见,所以 Rust 社区开发出一种关注分离的组织模式。这种模式可以用来组织任何用 Rust 构建的二进制项目,所以可以证明应该更早的开始这项重构,以为我们的项目符合这个模式。这个模式看起来像这样:
|
||||||
|
|
||||||
1. 将程序拆分成 *main.rs* 和 *lib.rs*。
|
1. 将程序拆分成 *main.rs* 和 *lib.rs*。
|
||||||
|
Loading…
Reference in New Issue
Block a user