wip start ch13

pull/4/head
KaiserY 8 years ago
parent 067e4de82d
commit 7c8b6ec817

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

@ -62,3 +62,11 @@
- [测试库的功能](ch12-04-testing-the-librarys-functionality.md)
- [处理环境变量](ch12-05-working-with-environment-variables.md)
- [输出到`stderr`而不是`stdout`](ch12-06-writing-to-stderr-instead-of-stdout.md)
## Rust 编程思想
- [Rust 中的函数式语言功能](ch13-00-functional-features.md)
- [闭包](ch13-01-closures.md)
- [迭代器](ch13-02-iterators.md)
- [改进 I/O 项目](ch13-03-improving-our-io-project.md)
- [性能](ch13-04-performance.md)

@ -81,3 +81,145 @@ search string and the lines of the contents to lowercase before comparing them
首先,将`search`字符串转换为小写,并存放于一个同名的覆盖变量中。注意现在`search`是一个`String`而不是字符串 slice所以在将`search`传递给`contains`时需要加上 &,因为`contains`获取一个字符串 slice。
接着在检查每个`line`是否包含`search`之前增加了一个`to_lowercase`调用。因为将`line`和`search`都转换为小写,我们就可以无视大小写的匹配文件和命令行参数了。看看测试是否通过了:
```
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
Running target\debug\deps\greprs-e58e9b12d35dc861.exe
running 2 tests
test test::case_insensitive ... ok
test test::case_sensitive ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured
Running target\debug\greprs-8a7faa2662b5030a.exe
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
Doc-tests greprs
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
```
好的!现在,我们必须真正的使用新的`grep_case_insensitive`函数。首先,在`Config`结构体中增加一个配置项:
<span class="filename">Filename: src/lib.rs</span>
```rust
pub struct Config {
pub search: String,
pub filename: String,
pub case_sensitive: bool,
}
```
<!-- Will add ghosting in libreoffice /Carol -->
接着在`run`函数中检查这个选项,并根据`case_sensitive`函数的值来决定调用哪个函数:
<span class="filename">Filename: src/lib.rs</span>
```rust,ignore
pub fn run(config: Config) -> Result<(), Box<Error>>{
let mut f = File::open(config.filename)?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
let results = if config.case_sensitive {
grep(&config.search, &contents)
} else {
grep_case_insensitive(&config.search, &contents)
};
for line in results {
println!("{}", line);
}
Ok(())
}
```
<!-- Will add ghosting in libreoffice /Carol -->
最后需要真正的检查环境变量。为了将标准库中的`env`模块引入作用域,在 *src/lib.rs* 开头增加一个`use`行:
<span class="filename">Filename: src/lib.rs</span>
```rust
use std::env;
```
并接着在`Config::new`中使用`env`模块的`vars`方法:
<span class="filename">Filename: src/lib.rs</span>
```rust
# use std::env;
#
# struct Config {
# search: String,
# filename: String,
# case_sensitive: bool,
# }
#
impl Config {
pub fn new(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("not enough arguments");
}
let search = args[1].clone();
let filename = args[2].clone();
let mut case_sensitive = true;
for (name, _) in env::vars() {
if name == "CASE_INSENSITIVE" {
case_sensitive = false;
}
}
Ok(Config {
search: search,
filename: filename,
case_sensitive: case_sensitive,
})
}
}
```
<!-- Will add ghosting and wingdings in libreoffice /Carol -->
这里我们调用了`env::vars`,它与`env::args`的工作方式类似。区别是`env::vars`返回一个环境变量而不是命令行参数的迭代器。不同于使用`collect`来创建一个所有环境变量的 vector我们使用`for`循环。`env::vars`返回一系列元组:环境变量的名称和其值。我们从来也不关心它的值,只关心它是否被设置了,所以可以使用`_`占位符来取代变量名来让 Rust 知道它不应该警告一个未使用的变量。最后,有一个默认为真的变量`case_sensitive`。如果我们找到了一个`CASE_INSENSITIVE`环境变量,就将`case_sensitive`设置为假。接着将其作为`Config`的一部分返回。
尝试运行几次吧!
```
$ cargo run to poem.txt
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target\debug\greprs.exe to poem.txt`
Are you nobody, too?
How dreary to be somebody!
```
```
$ CASE_INSENSITIVE=1 cargo run to poem.txt
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target\debug\greprs.exe to poem.txt`
Are you nobody, too?
How dreary to be somebody!
To tell your name the livelong day
To an admiring bog!
```
好极了!`greprs`现在可以通过环境变量的控制来进行大小写不敏感搜索了。现在你已经知道如何处理命令行参数或环境变量了!
一些程序允许对相同配置同时使用参数_和_环境变量。在这种情况下程序来决定参数和环境变量的优先级。作为一个留给你的测试尝试同时通过一个命令行参数来控制大小写不敏感搜索并在程序遇到矛盾值时决定其优先级。
`std::env`模块还包含了更多处理环境变量的实用功能;请查看官方文档来了解其可用的功能。

@ -0,0 +1,99 @@
## 输出到`stderr`而不是`stdout`
> [ch12-06-writing-to-stderr-instead-of-stdout.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch12-06-writing-to-stderr-instead-of-stdout.md)
> <br>
> commit 4f2dc564851dc04b271a2260c834643dfd86c724
目前为止,我们将所有的输出都`println!`到了终端。这是可以的,不过大部分终端都提供了两种输出:“标准输出”对应大部分信息,而“标准错误”则用于错误信息。这使得处理类似于“将错误打印到终端而将其他信息输出到文件”的情况变得更容易。
可以通过在命令行使用`>`来将输出重定向到文件中,同时不使用任何参数运行来造成一个错误,就会发现我们的程序只能打印到`stdout`
```
$ cargo run > output.txt
```
`>`语法告诉 shell 将标准输出的内容写入到 *output.txt* 文件中而不是打印到屏幕上。然而,如果运行命令后打开 *output.txt* 就会发现错误:
```
Problem parsing arguments: not enough arguments
```
我们希望这个信息被打印到屏幕上,而只有成功运行产生的输出写入到文件中。让我们如列表 12-17 中所示改变如何打印错误信息的方法:
<figure>
<span class="filename">Filename: src/main.rs</span>
```rust,ignore
extern crate greprs;
use std::env;
use std::process;
use std::io::prelude::*;
use greprs::Config;
fn main() {
let mut stderr = std::io::stderr();
let args: Vec<String> = env::args().collect();
let config = Config::new(&args).unwrap_or_else(|err| {
writeln!(
&mut stderr,
"Problem parsing arguments: {}",
err
).expect("Could not write to stderr");
process::exit(1);
});
if let Err(e) = greprs::run(config) {
writeln!(
&mut stderr,
"Application error: {}",
e
).expect("Could not write to stderr");
process::exit(1);
}
}
```
<figcaption>
Listing 12-17: Writing error messages to `stderr` instead of `stdout`
</figcaption>
</figure>
<!-- Will add ghosting and wingdings in libreoffice /Carol -->
Rust 并没有类似`println!`这样的方便写入标准错误的函数。相反,我们使用`writeln!`宏,它有点像`println!`,不过它获取一个额外的参数。第一个参数是希望写入内容的位置。可以通过`std::io::stderr`函数获取一个标准错误的句柄。我们将一个`stderr`的可变引用传递给`writeln!`;它需要是可变的因为这样才能写入信息!第二个和第三个参数就像`println!`的第一个和第二参数:一个格式化字符串和任何需要插入的变量。
让我们再次用相同方式运行程序,不带任何参数并用 `>`重定向`stdout`
```
$ cargo run > output.txt
Problem parsing arguments: not enough arguments
```
现在我们看到了屏幕上的错误信息,不过 `output.txt` 里什么也没有。如果我们使用正确的参数再次运行:
```
$ cargo run to poem.txt > output.txt
```
终端将没有输出,不过 `output.txt` 将会包含其结果:
<span class="filename">Filename: output.txt</span>
```
Are you nobody, too?
How dreary to be somebody!
```
## 总结
在这一章,我们涉及了如果在 Rust 中进行常规的 I/O 操作。通过使用命令行参数、文件、环境变量和写入`stderr`的功能。现在你已经准备好编写命令行程序了。结合前几章的知识,你的代码将会是组织良好的,并能有效的将数据存储到合适的数据结构中、更好的处理错误,并且还是经过良好测试的。我们也接触了一个真实情况下需要生命周期注解来保证引用一直有效的场景。
接下来,让我们探索如何利用一些 Rust 中受函数式编程语言影响的功能”闭包和迭代器。

@ -0,0 +1,6 @@
# Rust 中的函数式语言功能 —— 迭代器和闭包
> [ch13-00-functional-features.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch13-00-functional-features.md)
> <br>
> commit 4f2dc564851dc04b271a2260c834643dfd86c724
Loading…
Cancel
Save