Rust学习教程31 -返回值Result和?

本文详细介绍了Rust中的可恢复错误Result,包括如何处理返回的错误、unwrap和expect的使用、错误传播以及?操作符的妙用。通过示例代码,阐述了如何优雅地处理文件读写等操作中的错误,以及在不同场景下选择合适的方法进行错误处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文节选自<<Rust语言圣经>>一书
欢迎大家加入Rust编程学院,一起学习交流:
QQ群:1009730433

可恢复的错误Result

还记得上一节中,提到的关于文件读取的思考题吧?当时我们解决了读取中如果遇到不可恢复错误该怎么处理,现在来看看,读取过程中,正常返回和遇到可以恢复的错误时该如何处理。

假设,我们有一台消息服务器,每个用户都通过websocket连接到该服务器来接收和发送消息,该过程就涉及到socket文件的读写,那么此时,如果一个用户的读写发生了错误,显然不能直接panic,否则服务器会直接崩溃,所有用户都会断开连接,因此我们需要一种更温和的错误处理方式: Result<T,E>.

之前章节有提到过,Result<T,E>是一个枚举类型,定义如下:

enum Result<T, E> {
   
   
    Ok(T),
    Err(E),
}

泛型参数T代表成功时存入的正确值,存放方式是Ok(T)E代表错误是存入的错误值,存放方式是Err(E),枯燥的讲解永远不及代码生动准确,因此先来看下打开文件的例子:

use std::fs::File;

fn main() {
   
   
    let f = File::open("hello.txt");
}

以上File::open返回一个Result类型, 那么问题来了:

如何获知变量类型或者函数的返回类型

有几种常用的方式:

  • 第一种是查询标准库或者三方库文档,搜索File,然后找到它的open方法,但是此处更推荐第二种方法:
  • Rust IDE章节,我们推荐了VSCode IED和rust-analyze插件,如果你成功安装的话,那么就可以在VScode中很方便的通过代码跳转的方式查看代码,同时rust-analyze插件还会对代码中的类型进行标注,非常方便好用!
  • 你还可以尝试故意标记一个错误的类型,然后让编译器告诉你:
let f: u32 = File::open("hello.txt");

错误提示如下:

error[E0308]: mismatched types
 --> src/main.rs:4:18
  |
4 |     let f: u32 = File::open("hello.txt");
  |                  ^^^^^^^^^^^^^^^^^^^^^^^ expected u32, found enum
`std::result::Result`
  |
  = note: expected type `u32`
             found type `std::result::Result<std::fs::File, std::io::Error>`

上面代码,故意将f类型标记成整形,编译器立刻不乐意了,你是在忽悠我吗?打开文件操作返回一个整形?来,大哥来告诉你返回什么:std::result::Result<std::fs::File, std::io::Error>,我的天呐,怎么这么长的类型!

别慌,其实很简单,首先Result本身是定义在std::result中的,但是因为Result很常用,就被包含在了prelude中(将常用的东东提前引入到当前作用域内),因此无需手动引入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枚举可以提供:

use std::fs::File;

fn main() {
   
   
    let f = File::open("hello.txt");

    let f = match f {
   
   
        Ok(file) => file,
        Err(error) => {
   
   
            panic!("Problem opening the file: {:?}", error)
        },
    };
}

代码很清晰,对打开文件后的Result<T,E>类型进行匹配取值,如果是成功,则将Ok(file)中存放的的文件句柄file赋值给f,如果失败,则将Err(error)中存放的错误信息error使用panic抛出来,进而结束程序,这非常符合上文提到过的panic使用场景。

好吧,也没有那么合理:)

对返回的错误进行处理

直接panic还是过于粗暴,因为实际上IO的错误有很多种,我们需要对部分错误进行特殊处理,而不是所有错误都直接崩溃:

use std::fs::File;
use std::io::ErrorKind;

fn main() {
   
   
    let f = File::open(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值