@ -1,4 +1,4 @@
## 重构改进模块性和错误处理
# ## 重构改进模块性和错误处理
> [ch12-03-improving-error-handling-and-modularity.md ](https://github.com/rust-lang/book/blob/master/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/src/ch12-03-improving-error-handling-and-modularity.md )
> < br >
> < br >
@ -10,7 +10,7 @@
这同时也关系到第二个问题:`search` 和 `filename` 是程序中的配置变量,而像 `contents` 则用来执行程序逻辑。随着 `main` 函数的增长,就需要引入更多的变量到作用域中,而当作用域中有更多的变量时,将更难以追踪每个变量的目的。最好能将配置变量组织进一个结构,这样就能使他们的目的更明确了。
这同时也关系到第二个问题:`search` 和 `filename` 是程序中的配置变量,而像 `contents` 则用来执行程序逻辑。随着 `main` 函数的增长,就需要引入更多的变量到作用域中,而当作用域中有更多的变量时,将更难以追踪每个变量的目的。最好能将配置变量组织进一个结构,这样就能使他们的目的更明确了。
第三个问题是如果打开文件失败我们使用 `expect` 来打印出错误信息,不过这个错误信息只是说 ` file not found`。除了缺少文件之外还有很多可以导致打开文件失败的方式:例如,文件可能存在,不过可能没有打开它的权限。如果我们现在就出于这种情况,打印出的 `file not found` 错误信息就给了用户错误的建议 !
第三个问题是如果打开文件失败我们使用 `expect` 来打印出错误信息,不过这个错误信息只是说 ` Something went wrong reading the file`。读取文件失败的原因有多种:例如文件不存在,或者没有打开此文件的权限。目前,无论处于何种情况,我们只是打印出“文件读取出现错误”的信息,这并没有给予使用者具体的信息 !
第四,我们不停地使用 `expect` 来处理不同的错误,如果用户没有指定足够的参数来运行程序,他们会从 Rust 得到 `index out of bounds` 错误,而这并不能明确地解释问题。如果所有的错误处理都位于一处,这样将来的维护者在需要修改错误处理逻辑时就只需要考虑这一处代码。将所有的错误处理都放在一处也有助于确保我们打印的错误信息对终端用户来说是有意义的。
第四,我们不停地使用 `expect` 来处理不同的错误,如果用户没有指定足够的参数来运行程序,他们会从 Rust 得到 `index out of bounds` 错误,而这并不能明确地解释问题。如果所有的错误处理都位于一处,这样将来的维护者在需要修改错误处理逻辑时就只需要考虑这一处代码。将所有的错误处理都放在一处也有助于确保我们打印的错误信息对终端用户来说是有意义的。
@ -241,7 +241,7 @@ impl Config {
#### `Config::new` 调用并处理错误
#### `Config::new` 调用并处理错误
为了处理错误情况并打印一个对用户友好的信息,我们需要像示例 12-10 那样更新 `main` 函数来处理现在 `Config::new` 返回的 `Result` 。另外还需要负责手动实现 `panic!` 的使用非零错误码退出命令行工具的工作。非零的退出状态是一个告诉调用程序的进程我们的程序以错误状态退出的惯例信号 。
为了处理错误情况并打印一个对用户友好的信息,我们需要像示例 12-10 那样更新 `main` 函数来处理现在 `Config::new` 返回的 `Result` 。另外还需要手动实现原先由 `panic!` 负责的工作,即以非零错误码退出命令行工具的工作。非零的退出状态是一个惯例信号,用来告诉调用程序的进程:该程序以错误状态退出了 。
< span class = "filename" > 文件名: src/main.rs< / span >
< span class = "filename" > 文件名: src/main.rs< / span >
@ -277,7 +277,7 @@ Problem parsing arguments: not enough arguments
### 从 `main` 提取逻辑
### 从 `main` 提取逻辑
现在我们完成了配置解析的重构:让我们转向程序的逻辑。正如 [“二进制项目的关注分离” ](#separation-of-concerns-for-binary-projects ) 部分所展开的讨论,我们将提取一个叫做 `run` 的函数来存放目前 `main ` 函数中不属于设置配置或处理错误的所有逻辑。一旦完成这些,`main` 函数将简明的 足以通过观察来验证,而我们将能够为所有其他逻辑编写测试。
现在我们完成了配置解析的重构:让我们转向程序的逻辑。正如 [“二进制项目的关注分离” ](#separation-of-concerns-for-binary-projects ) 部分所展开的讨论,我们将提取一个叫做 `run` 的函数来存放目前 `main ` 函数中不属于设置配置或处理错误的所有逻辑。一旦完成这些,`main` 函数将简明得 足以通过观察来验证,而我们将能够为所有其他逻辑编写测试。
示例 12-11 展示了提取出来的 `run` 函数。目前我们只进行小的增量式的提取函数的改进。我们仍将在 *src/main.rs* 中定义这个函数:
示例 12-11 展示了提取出来的 `run` 函数。目前我们只进行小的增量式的提取函数的改进。我们仍将在 *src/main.rs* 中定义这个函数:
@ -335,7 +335,7 @@ fn run(config: Config) -> Result<(), Box<dyn Error>> {
第二个改变是去掉了 `expect` 调用并替换为 [第九章][ch9-question-mark] 讲到的 `?` 。不同于遇到错误就 `panic!` , `?` 会从函数中返回错误值并让调用者来处理它。
第二个改变是去掉了 `expect` 调用并替换为 [第九章][ch9-question-mark] 讲到的 `?` 。不同于遇到错误就 `panic!` , `?` 会从函数中返回错误值并让调用者来处理它。
第三个修改是现在成功时这个函数会返回一个 `Ok` 值。因为 `run` 函数签名中声明成功类型返回值是 `()` ,这意味着需要将 unit 类型值包装进 `Ok` 值中。`Ok(())` 一开始看起来有点奇怪,不过这样使用 `()` 是表明我们调用 `run` 只是为了它的副作用的惯用方式;它 并没有返回什么有意义的值。
第三个修改是现在成功时这个函数会返回一个 `Ok` 值。因为 `run` 函数签名中声明成功类型返回值是 `()` ,这意味着需要将 unit 类型值包装进 `Ok` 值中。`Ok(())` 一开始看起来有点奇怪,不过这样使用 `()` 是惯用的做法,表明调用 `run` 函数只是为了它的副作用;函数 并没有返回什么有意义的值。
上述代码能够编译,不过会有一个警告:
上述代码能够编译,不过会有一个警告:
@ -350,7 +350,7 @@ warning: unused `std::result::Result` that must be used
= note: this `Result` may be an `Err` variant, which should be handled
= note: this `Result` may be an `Err` variant, which should be handled
```
```
Rust 提示我们的代码忽略了 `Result` 值,它可能表明这里存在一个错误。虽然我们 没有检查这里是否有一个错误,而编译器提醒我们这里应该有一些错误处理代码!现在就让我们修正这个问题。
Rust 提示我们的代码忽略了 `Result` 值,它可能表明这里存在一个错误。但我们却 没有检查这里是否有一个错误,而编译器提醒我们这里应该有一些错误处理代码!现在就让我们修正这个问题。
#### 处理 `main` 中 `run` 返回的错误
#### 处理 `main` 中 `run` 返回的错误
@ -438,7 +438,7 @@ fn main() {
我们添加了一行 `use minigrep::Config` ,它将 `Config` 类型引入作用域,并使用 crate 名称作为 `run` 函数的前缀。通过这些重构,所有功能应该能够联系在一起并运行了。运行 `cargo run` 来确保一切都正确的衔接在一起。
我们添加了一行 `use minigrep::Config` ,它将 `Config` 类型引入作用域,并使用 crate 名称作为 `run` 函数的前缀。通过这些重构,所有功能应该能够联系在一起并运行了。运行 `cargo run` 来确保一切都正确的衔接在一起。
哇哦!这可有很多 的工作,不过我们为将来的成功打下了基础。现在处理错误将更容易,同时代码也更加模块化。从现在开始几乎所有的工作都将在 *src/lib.rs* 中进行。
哇哦!我们做了大量 的工作,不过我们为将来的成功打下了基础。现在处理错误将更容易,同时代码也更加模块化。从现在开始几乎所有的工作都将在 *src/lib.rs* 中进行。
让我们利用这些新创建的模块的优势来进行一些在旧代码中难以展开的工作,这些工作在新代码中非常容易实现,那就是:编写测试!
让我们利用这些新创建的模块的优势来进行一些在旧代码中难以展开的工作,这些工作在新代码中非常容易实现,那就是:编写测试!