Update result.md

pull/236/head
Jesse 3 years ago committed by GitHub
parent 48a73f4043
commit a99a664b85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,5 @@
# 可恢复的错误Result # 可恢复的错误Result
还记得上一节中,提到的关于文件读取的思考题吧?当时我们解决了读取中如果遇到不可恢复错误的问题,现在来看看,读取过程中,正常返回和遇到可以恢复的错误时该如何处理。 还记得上一节中,提到的关于文件读取的思考题吧?当时我们解决了读取文件时遇到不可恢复错误该怎么处理的问题,现在来看看,读取过程中,正常返回和遇到可以恢复的错误时该如何处理。
假设,我们有一台消息服务器,每个用户都通过 websocket 连接到该服务器来接收和发送消息,该过程就涉及到 socket 文件的读写那么此时如果一个用户的读写发生了错误显然不能直接panic否则服务器会直接崩溃所有用户都会断开连接因此我们需要一种更温和的错误处理方式`Result<T,E>`。 假设,我们有一台消息服务器,每个用户都通过 websocket 连接到该服务器来接收和发送消息,该过程就涉及到 socket 文件的读写那么此时如果一个用户的读写发生了错误显然不能直接panic否则服务器会直接崩溃所有用户都会断开连接因此我们需要一种更温和的错误处理方式`Result<T,E>`。
@ -20,7 +20,7 @@ fn main() {
``` ```
以上 `File::open` 返回一个 `Result` 类型,那么问题来了: 以上 `File::open` 返回一个 `Result` 类型,那么问题来了:
> #### 如何获变量类型或者函数的返回类型 > #### 如何获变量类型或者函数的返回类型
> >
> 有几种常用的方式,此处更推荐第二种方法: > 有几种常用的方式,此处更推荐第二种方法:
> - 第一种是查询标准库或者三方库文档,搜索 `File`,然后找到它的 `open` 方法 > - 第一种是查询标准库或者三方库文档,搜索 `File`,然后找到它的 `open` 方法
@ -46,7 +46,7 @@ error[E0308]: mismatched types
别慌,其实很简单,首先 `Result` 本身是定义在 `std::result` 中的,但是因为 `Result` 很常用,所以就被包含在了[`prelude`](../../appendix/prelude.md)中(将常用的东东提前引入到当前作用域内),因此无需手动引入 `std::result::Result`,那么返回类型可以简化为 `Result<std::fs::File,std::io::Error>`,你看看是不是很像标准的 `Result<T,E>` 枚举定义?只不过 `T` 被替换成了具体的类型 `std::fs::File`,是一个文件句柄类型,`E` 被替换成 `std::io::Error`,是一个 IO 错误类型. 别慌,其实很简单,首先 `Result` 本身是定义在 `std::result` 中的,但是因为 `Result` 很常用,所以就被包含在了[`prelude`](../../appendix/prelude.md)中(将常用的东东提前引入到当前作用域内),因此无需手动引入 `std::result::Result`,那么返回类型可以简化为 `Result<std::fs::File,std::io::Error>`,你看看是不是很像标准的 `Result<T,E>` 枚举定义?只不过 `T` 被替换成了具体的类型 `std::fs::File`,是一个文件句柄类型,`E` 被替换成 `std::io::Error`,是一个 IO 错误类型.
这个返回值类型说明 `File::open` 调用如果成功则返回一个可以进行读写的文件句柄,如果失败,则返回一个 IO 错误:文件不存在或者没有访问文件的权限等。总之 `File::open` 需要一个方式告知调用者是成功还是失败,并同时返回具体的文件句柄(成功)或错误信息(失败),这些信息通可以通过 `Result` 枚举提供: 这个返回值类型说明 `File::open` 调用如果成功则返回一个可以进行读写的文件句柄,如果失败,则返回一个 IO 错误:文件不存在或者没有访问文件的权限等。总之 `File::open` 需要一个方式告知调用者是成功还是失败,并同时返回具体的文件句柄(成功)或错误信息(失败)万幸的是,这些信息通可以通过 `Result` 枚举提供:
```rust ```rust
use std::fs::File; use std::fs::File;
@ -200,11 +200,11 @@ let mut f = match f {
``` ```
如果结果是 `Ok(T)`,则把 `T` 赋值给 `f`,如果结果是 `Err(E)`,则返回该错误,所以 `?` 特别适合用来传播错误。 如果结果是 `Ok(T)`,则把 `T` 赋值给 `f`,如果结果是 `Err(E)`,则返回该错误,所以 `?` 特别适合用来传播错误。
虽然 `?``match` 功能一致,但是事实上 `?` 会更胜一筹。这应该怎么理解呢 虽然 `?``match` 功能一致,但是事实上 `?` 会更胜一筹。何解
想象一下,一个设计良好的系统中,肯定有自定义的错误特征,错误之间很可能会存在上下级关系,例如标准库中的 `std::io::Error ``std::error::Error`前者是io相关的错误结构体后者是一个最最通用的标准错误特征同时前者实现了后者因此 `std::io::Error` 可以转换为 `std:error::Error` 想象一下,一个设计良好的系统中,肯定有自定义的错误特征,错误之间很可能会存在上下级关系,例如标准库中的 `std::io::Error ``std::error::Error`前者是io相关的错误结构体后者是一个最最通用的标准错误特征同时前者实现了后者因此 `std::io::Error` 可以转换为 `std:error::Error`
明白了以上的错误转换,`?` 的更胜一筹就很好理解了,它可以自动进行类型转换: 明白了以上的错误转换,`?` 的更胜一筹就很好理解了,它可以自动进行类型提升(转换
```rust ```rust
fn open_file() -> Result<File, Box<dyn std::error::Error>> { fn open_file() -> Result<File, Box<dyn std::error::Error>> {
let mut f = File::open("hello.txt")?; let mut f = File::open("hello.txt")?;

Loading…
Cancel
Save