Rust的可靠性
错误处理
- 程序会打印一个错误信息
- 展开(unwind)、清理调用栈
- 退出程序
这其中在执行第二步的时候会特别浪费性能,我们可以选择中止(abort)调用栈(默认是开启的展开清理调用栈)。
程序展开调用栈(工作量大)
- 不进行清理,直接停止程序
- 内存需要OS进行清理
设置方法:
- 在Cargo.toml中将profile部分进行设置:
- panic = ‘abort’
// Cargo.toml文件
[profile.release]
panic = 'abort'
- panic = ‘abort’
可恢复错误与Result
Result 就是一个枚举
enum Result {
Ok(T),
Err(E),
}
fn main() { let f = File::open(“hello.txt”); // 利用match来匹配Result对应的类型做处理 let f = match f { Ok(file) => file, Err(e) => { panic!(“错误信息为:{:?}”, e); } }; }
对于上面这种错误,我们可以继续细分错误类型,来进行不同的处理 <br />比如我们可以在错误类型为NotFound的时候,去创建对应的文件 <br />判断错误类型,标准库里也提供了对应的方法,就是std::io::ErrorKind
```rust
use std::{fs::File, io::ErrorKind};
fn main() {
let f = File::open("README.md");
let f = match f {
Ok(file) => file,
Err(e) => match e.kind() {
// 错误类型如果是NotFound,就创建文件
ErrorKind::NotFound => match File::create("hello.txt") {
// 匹配创建文件时的Result
Ok(fc) => fc,
Err(err) => panic!("文件创建失败{:?}", err),
},
other_error => panic!("错误信息为:{:?}", other_error),
},
};
print!("{:?}", f)
}
可以看出来其实我们上面利用match
方法进行的错误处理,代码量是很大的,写起来很繁琐。
所以Rust
提供了一些简单的方法让我们来完成类似的工作
利用unwrap替代match
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
// 利用match来匹配Result对应的类型做处理
let f = match f {
Ok(file) => file,
Err(e) => {
panic!("错误信息为:{:?}", e);
}
};
}
//利用unwrap改造
fn main(){
let f = File::open("hello.txt").unwrap();
}
从上面这个例子其实就可以看出来,unwrap 的作用:
- 如果Result结果是Ok,返回Ok里的值
- 如果Result结果是Err,调用painc!宏
当然unwrap 也有缺点,就是不能自定义错误信息。所以expect 就横空出世
//利用expect改造
fn main(){
let f = File::open("hello.txt").expect("自定义错误信息");
}
传播错误和?
当你的程序中出现错误的时候,不仅可以处理错误,还可以将错误传播给调用者,让调用者知道该错误的发生
use std::{
fs::File,
io::{self, Read},
};
fn main() {
let f = read_username_from_file("hello.txt".to_string());
print!("{:?}", f)
}
// 该函数会将错误返回给调用者
fn read_username_from_file(path: String) -> Result<String, io::Error> {
// 首先读取文件
let f = File::open(path);
// 匹配Result
let mut f = match f {
Ok(file) => file,
Err(error) => return Err(error),// 这里返回错误
};
// 创建需要返回的String
let mut s = String::new();
// 读取文件内容(这里也是将匹配结果返回)
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(err) => Err(err),
}
}
- 如果发生错误,例如文件没找到,就会打印错误
- 如果没有发生错误,就会打印文件内容
但是从上面的例子可以看出,传播错误的代码也很繁琐,所以rust又提供了一个语法糖? 来帮助我们完成传播错误这一操作。
use std::{
fs::File,
io::{self, Read},
};
fn main() {
let f = read_username_from_file("hello.txt".to_string());
print!("{:?}", f)
}
fn read_username_from_file(path: String) -> Result<String, io::Error> {
// 首先读取文件,利用?来传播错误
let mut f = File::open(path)?;
// 创建需要返回的String
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
从上面的代码实例可以看出,? 的作用其实很简单:
- 如果Result类型是Ok的话,就会返回Ok的类型值(这里的返回其实相当于match的返回,函数里的代码还会正常执行)
- 如果Result类型是Err的话,就会把Err(e) 作为整个函数的返回值进行返回(这里的返回就是函数执行到这里就结束了,不会往下执行了,整个函数的结果已经产生了)
? 与from函数:let mut f = File::open(path)?; // 两段代码的效果是一样的 let mut f = match f { Ok(file) => file, Err(error) => return Err(error),// 这里返回错误 };
- 被?所应用的错误,会隐式的调用from函数处理
- 当?调用from函数时:他所接收的错误类型就会转换为当前函数返回类型定义的错误类型
就相当于我们将错误类型A转换成了Result