[易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理]
[易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理]
实用知识
错误处理
我们今天来讲讲Rust中的错误处理。
很多语言都有自己的错误处理方式,比如,java是异常处理机制。
Rust有自己独特的错误处理机制。
在Rust有两种错误: recoverable and unrecoverable errors.
翻译成中文就是:可恢复错误和不可恢复错误。
Rust分别用两种方式来处理这两种错误:
1.Result用来处理可恢复错误,这种方式一般不会直接退出当前程序。
2.宏panic!用来处理不可恢复错误,这种方式一般会直接退出当前程序。
什么是宏?
宏是用来生成代码的,在调用宏的地方,编译器会先将宏进行展开,生成代码,然后再编译展开后的代码。
我们接下来的一篇文章会讲到,现在只要记住它是用来生成代码的,就行了。
回到正题。
我们先来看看panic!,先看看简单的代码:
fn main() {
panic!("crash and burn");
}
运行代码,会产生这样的结果:
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/panic`
thread 'main' panicked at 'crash and burn', src/main.rs:2:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.
这里的信息,明确地指出panic发生的代码行。
我们再看看另一个std标准库的例子:
fn main() {
let v = vec![1, 2, 3];
v[99];
}
运行代码,我们又会产生这样的结果:
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/panic`
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', libcore/slice/mod.rs:2448:10
note: Run with `RUST_BACKTRACE=1` for a backtrace.
这段代码,直接访问超出vector下标的元素,系统直接报告一个不可恢复的错误,并直接退出程序。
我们再来看看Result。
先看看简单的例子:
use std::fs::File;
fn main() {
let f = 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>`
这段错误信息告诉我们,我们的代码要有返回值 :Result<T, E>
那好,我们来修改代码:
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的unwrap),这种 写法我们在下面会详细说明 :
use std::fs::File;
fn main() {
let f = File::open("hello.txt").unwrap();
}
我们可以用模式匹配来处理,这种方法比java等语言的代码,简洁多了。漂亮!
我们还是从显式的模式匹配代码看看逻辑:
let _f = match f {
Ok(file) => file,
Err(error) => {
panic!("Problem opening the file: {:?}", error)
},
};
如果文件hello.txt在当前工程目录存在,匹配到:Ok,并返回文件句柄( file handle)。
如果文件不存在,就直接返回错误(panic!),并直接退出当前程序。
好,运行代码,得出结果:
thread 'main' panicked at 'Problem opening the file: Os { code: 2, kind: NotFound, message: "系统找不到指定的文件。" }', src\main.rs:123:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
error: process didn't exit successfully: `target\debug\hello.exe` (exit code: 101)
因为当前目录不存在文件hello.txt,所以程序直接返回panic错误。
好理解。
好,我们现在再改造一下代码:
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt");
let _fh = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {:?}", e),
},
other_error => panic!("Problem opening the file: {:?}", other_error),
},
};
}
我们再对不同的error再进行细粒度处理。
1.如果文件存在,就创建一个文件:hello.txt,那创建的过程中,又会出现 两种 情况 :
1.1创建成功,直接返回文件句柄。
1.2创建失败,直接调用panic!宏。
运行代码, 程序会自己在工程根目录创建一个空的文件:hello.txt。
我们看到上面 的代码,太多模式匹配,有点复杂,能不能再简洁一点?
可以的,请看下面代码:
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Problem creating the file: {:?}", error);
})
} else {
panic!("Problem opening the file: {:?}", error);
}
});
}
在Rust,我们用闭包让代码再简洁可读。完美!
其中unwrap_or_else方法,是Result这个枚举的方法。
它主要的用法是,打开Result,如果OK,就直接取OK中的内容,否则,
如果它是Err,则执行后面的闭包,请看基本用法的简单例子:
fn count(x: &str) -> usize { x.len() }
assert_eq!(Ok(2).unwrap_or_else(count), 2);
assert_eq!(Err("foo").unwrap_or_else(count), 3);
好的。
回到我们的panic错误处理。
Rust有两种简洁写法:unwrap
and expect
我们来一一分析下,先来看unwrap方法:
use std::fs::File;
fn main() {
let f = File::open("hello.txt").unwrap();
}
运行代码,返回错误:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error {
repr: Os { code: 2, message: "No such file or directory" } }',
src/libcore/result.rs:906:4
这里错误信息都是默认信息: message: "No such file or directory"
能不能自定义呢?
可以的。
用expect,请看例子:
use std::fs::File;
fn main() {
let f = File::open("hello.txt").expect("Failed to open hello.txt");
}
运行代码,返回错误:
thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
2, message: "No such file or directory" } }', src/libcore/result.rs:906:4
这里的错误信息,就我们自定义的:Failed to open hello.txt
很好!
用自定义的错误信息,可以让我们更好地定位问题。
错误信息的传播
我们再来看看这样的代码:
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
这里我们定义一个方法,用来从文件中读username。
这里我们用了模式匹配代码,逻辑很清楚,但很复杂很繁琐。
有没有更好的写法?
有的。
在Rust,我们可以这样写:
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
所有模式匹配处理错误的代码,都可以用?替换,简洁很多!很好!
有没有更简洁的方式 ?
有的。
请看下面代码:
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
我们可以用链式方法。漂亮!
当然,更好的方式,是直接用标准库里的方法,那就更简洁了:
use std::io;
use std::fs;
fn read_username_from_file() -> Result<String, io::Error> {
fs::read_to_string("hello.txt")
}
fn main() {
let r = read_username_from_file();
println!("the string in hello.txt is {:?}", r);
}
实际项目开发中,我们更推荐直接用标准库里的方法。因为标准库的方法,更少出错,经过更多测试,久经考验!
我们这里要特别强调一点就是:?只能用于有返回值并且返回值为Result<T,E>(或Option)的方法。
不信?
我们来做做实验:
use std::fs::File;
fn main() {
let f = File::open("hello.txt")?;
}
运行上面的代码,会报错误:
error[E0277]: the `?` operator can only be used in a function that returns
`Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/main.rs:4:13
|
4 | let f = File::open("hello.txt")?;
| ^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a
function that returns `()`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
怎么办?
改造一下代码:
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("hello.txt")?;
Ok(())
}
这里的Box<>,是智能指针,它用来封装不同的错误类型。
现在main函数可以正常编译了。
以上,希望对你有用。
如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust
参考文章:
https://doc.rust-lang.org/stable/book/ch09-02-recoverable-errors-with-result.html
https://learning-rust.github.io/docs/e2.panicking.html
[易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理]的更多相关文章
- [易学易懂系列|rustlang语言|零基础|快速入门|(28)|实战5:实现BTC价格转换工具]
[易学易懂系列|rustlang语言|零基础|快速入门|(28)|实战5:实现BTC价格转换工具] 项目实战 实战5:实现BTC价格转换工具 今天我们来开发一个简单的BTC实时价格转换工具. 我们首先 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链]
[易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链] 项目实战 实战4:从零实现BTC区块链 我们今天来开发我们的BTC区块链系统. 简单来说,从数据结构的 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)]
[易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)] 项目实战 实战3:Http服务器 我们今天来进一步开发我们的Http服务器,用多线程实现. 我 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)]
[易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)] 项目实战 实战2:命令行工具minigrep 我们继续开发我们的minigrep. 我们现 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(24)|实战2:命令行工具minigrep(1)]
[易学易懂系列|rustlang语言|零基础|快速入门|(24)|实战2:命令行工具minigrep(1)] 项目实战 实战2:命令行工具minigrep 有了昨天的基础,我们今天来开始另一个稍微有点 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(23)|实战1:猜数字游戏]
[易学易懂系列|rustlang语言|零基础|快速入门|(23)|实战1:猜数字游戏] 项目实战 实战1:猜数字游戏 我们今天来来开始简单的项目实战. 第一个简单项目是猜数字游戏. 简单来说,系统给了 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(5)|生命周期Lifetime]
[易学易懂系列|rustlang语言|零基础|快速入门|(5)] Lifetimes 我们继续谈谈生命周期(lifttime),我们还是拿代码来说话: fn main() { let mut a = ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro]
[易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro] 实用知识 宏Macro 我们今天来讲讲Rust中强大的宏Macro. Rust的宏macro是实现元编程的强大工具. ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针]
[易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针] 实用知识 智能指针 我们今天来讲讲Rust中的智能指针. 什么是指针? 在Rust,指针(普通指针),就是保存内存地址的值 ...
随机推荐
- [转] 运维知识体系 -v3.1 作者:赵舜东(赵班长)转载请注明来自于-新运维社区:https://www.unixhot.com
[From]https://www.unixhot.com/page/ops [运维知识体系]-v3.1 作者:赵舜东(赵班长) (转载请注明来自于-新运维社区:https://www.unixhot ...
- 如何为Redis中list中的项设置过期时间
问题 两种解决方法 有序集合 多个集合以及TTL Redis是一个伟大的工具,用来在内存中存储列表是很合适的. 不过,如果你想要快速搜索列表,同时需要让列表中每项都在一定时间后过期,应该怎么做呢? 首 ...
- Python报错module 'scipy.misc' has no attribute 'xxx'
Python报错module 'scipy.misc' has no attribute 'imresize' 解决办法: 安装Pillow包,命令如下: pip install Pillow 然后重 ...
- Leetcode之动态规划(DP)专题-338. 比特位计数(Counting Bits)
Leetcode之动态规划(DP)专题-338. 比特位计数(Counting Bits) 给定一个非负整数 num.对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数 ...
- WDS部署基础知识:使用WDS捕获与应用映像(使用WDS定制系统和应用)
WDS部署基础知识:使用WDS捕获与应用映像(使用WDS定制系统和应用) Win7部署基础知识(8):使用WDS捕获与应用映像 一.添加映像组 使用WDS捕获映像时,会将映像加载到WDS服务器的映像 ...
- 【神经网络与深度学习】【C/C++】C++日志操作开源函数库之Google-glog
今天想给我的C++项目找一个开源的日志类,用于记录系统日志,结果浪费了半个下午的时间.从网上搜索相关资料,找到以下几个备选方案: 1.log4cplus 下载地址:http://sourceforge ...
- Java中的锁-悲观锁、乐观锁,公平锁、非公平锁,互斥锁、读写锁
总览图 如果文中内容有错误,欢迎指出,谢谢. 悲观锁.乐观锁 悲观锁.乐观锁使用场景是针对数据库操作来说的,是一种锁机制. 悲观锁(Pessimistic Lock):顾名思义,就是很悲观,每次去拿数 ...
- MSF魔鬼训练营-3.4.2网络漏洞扫描-OpenVAS(待补完)PS:在虚拟机里面运行OpenVAS扫描的进度真的是超慢啊...
由于新版的kali中没有预装OpenVAS.所以在虚拟机中安装花了非常多的时间. 安装过程参考:http://www.cnblogs.com/zlslch/p/6872559.html过程写的非常详 ...
- GS7 安装使用Oracle19c 客户端的说明
1. 最近Oracle放出了 windows版本的oracle19c的安装文件(具体时间不详, 自己知道的时候比较晚了) 2. 发现文件其实比较多如图示: 3. 经过自己测试实现发现 不能使用 如下 ...
- python computer info look
计算机信息查看-. import platform import platform def TestPlatform(): print("---------SYSTEM INFO------ ...