Merge pull request #424 from Nangos/patch-13

Update ch09-02-recoverable-errors-with-result.md
This commit is contained in:
KaiserY 2020-06-27 21:41:19 +08:00 committed by GitHub
commit 427c750725
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -121,9 +121,9 @@ fn main() {
<span class="caption">示例 9-5使用不同的方式处理不同类型的错误</span>
`File::open` 返回的 `Err` 成员中的值类型 `io::Error`,它是一个标准库中提供的结构体。这个结构体有一个返回 `io::ErrorKind` 值的 `kind` 方法可供调用。`io::ErrorKind` 是一个标准库提供的枚举,它的成员对应 `io` 操作可能导致的不同错误类型。我们感兴趣的成员是 `ErrorKind::NotFound`,它代表尝试打开的文件并不存在。所以 `match``f` 匹配,不过对于 `error.kind()` 还有一个内部 `match`
`File::open` 返回的 `Err` 成员中的值类型 `io::Error`,它是一个标准库中提供的结构体。这个结构体有一个返回 `io::ErrorKind` 值的 `kind` 方法可供调用。`io::ErrorKind` 是一个标准库提供的枚举,它的成员对应 `io` 操作可能导致的不同错误类型。我们感兴趣的成员是 `ErrorKind::NotFound`,它代表尝试打开的文件并不存在。这样,`match` 就匹配完 `f` 了,不过对于 `error.kind()` 还有一个内层 `match`
我们希望在匹配守卫中检查的条件是 `error.kind()` 的返回值是 `ErrorKind``NotFound` 成员。如果是,则尝试通过 `File::create` 创建文件。然而因为 `File::create` 也可能会失败,还需要增加一个内`match` 语句。当文件不能被打开,会打印出一个不同的错误信息。外部 `match` 的最后一个分支保持不变,这样对任何除了文件不存在的错误会使程序 panic。
我们希望在内层 `match` 中检查的条件是 `error.kind()` 的返回值是否为 `ErrorKind``NotFound` 成员。如果是,则尝试通过 `File::create` 创建文件。然而因为 `File::create` 也可能会失败,还需要增加一个内`match` 语句。当文件不能被打开,会打印出一个不同的错误信息。外层 `match` 的最后一个分支保持不变,这样对任何除了文件不存在的错误会使程序 panic。
这里有好多 `match``match` 确实很强大不过也非常的基础。第十三章我们会介绍闭包closure。`Result<T, E>` 有很多接受闭包的方法,并采用 `match` 表达式实现。一个更老练的 Rustacean 可能会这么写:
@ -144,7 +144,7 @@ fn main() {
}
```
虽然这段代码有着如示例 9-5 一样的行为,但并没有包任何 `match` 表达式且更容易阅读。在阅读完第十三章后再回到这个例子,并查看标准库文档 `unwrap_or_else` 方法都做了什么操作。在处理错误时,还有很多这类方法可以消除大量嵌套的 `match` 表达式。
虽然这段代码有着如示例 9-5 一样的行为,但并没有包任何 `match` 表达式且更容易阅读。在阅读完第十三章后再回到这个例子,并查看标准库文档 `unwrap_or_else` 方法都做了什么操作。在处理错误时,还有很多这类方法可以消除大量嵌套的 `match` 表达式。
### 失败时 panic 的简写:`unwrap` 和 `expect`
@ -254,7 +254,7 @@ fn read_username_from_file() -> Result<String, io::Error> {
`Result` 值之后的 `?` 被定义为与示例 9-6 中定义的处理 `Result` 值的 `match` 表达式有着完全相同的工作方式。如果 `Result` 的值是 `Ok`,这个表达式将会返回 `Ok` 中的值而程序将继续执行。如果值是 `Err``Err` 中的值将作为整个函数的返回值,就好像使用了 `return` 关键字一样,这样错误值就被传播给了调用者。
示例 9-6 中的 `match` 表达式与问号运算符所做的有一点不同:`?` 运算符所使用的错误值被传递给了 `from` 函数,它定义于标准库的 `From` trait 中,其用来将错误从一种类型转换为另一种类型。当 `?` 运算符调用 `from` 函数时,收到的错误类型被转换为定义为当前函数返回的错误类型。这在当一个函数返回一个错误类型来代表所有可能失败的方式时很有用,即使其可能会因很多种原因失败。只要每一个错误类型都实现了 `from` 函数来定义如将其转换为返回的错误类型,`?` 运算符会自动处理这些转换。
示例 9-6 中的 `match` 表达式与问号运算符所做的有一点不同:`?` 运算符所使用的错误值被传递给了 `from` 函数,它定义于标准库的 `From` trait 中,其用来将错误从一种类型转换为另一种类型。当 `?` 运算符调用 `from` 函数时,收到的错误类型被转换为由当前函数返回类型所指定的错误类型。这在当函数返回单个错误类型来代表所有可能失败的方式时很有用,即使其可能会因很多种原因失败。只要每一个错误类型都实现了 `from` 函数来定义如何将自身转换为返回的错误类型,`?` 运算符会自动处理这些转换。
在示例 9-7 的上下文中,`File::open` 调用结尾的 `?` 将会把 `Ok` 中的值返回给变量 `f`。如果出现了错误,`?` 运算符会提早返回整个函数并将一些 `Err` 值传播给调用者。同理也适用于 `read_to_string` 调用结尾的 `?`
@ -278,7 +278,7 @@ fn read_username_from_file() -> Result<String, io::Error> {
<span class="caption">示例 9-8问号运算符之后的链式方法调用</span>
`s` 中创建新的 `String` 被放到了函数开头;这一部分没有变化。我们对 `File::open("hello.txt")?` 的结果直接链式调用了 `read_to_string`,而不再创建变量 `f`。仍然需要 `read_to_string` 调用结尾的 `?`,而且当 `File::open``read_to_string` 都成功没有失败时返回包含用户名 `s``Ok` 值。其功能再一次与示例 9-6 和示例 9-7 保持一致,不过这是一个与众不同且更符合工程学的写法。
`s` 中创建新的 `String` 被放到了函数开头;这一部分没有变化。我们对 `File::open("hello.txt")?` 的结果直接链式调用了 `read_to_string`,而不再创建变量 `f`。仍然需要 `read_to_string` 调用结尾的 `?`,而且当 `File::open``read_to_string` 都成功没有失败时返回包含用户名 `s``Ok` 值。其功能再一次与示例 9-6 和示例 9-7 保持一致,不过这是一个与众不同且更符合工程学(ergonomic)的写法。
说到编写这个函数的不同方法,甚至还有一个更短的写法: