错误分为可恢复错误与不可恢复错误。可恢复错误就是并不会影响整个程序执行的错误,如文件打开失败,那么重试即可。而不可恢复错误,就是发生了那么程序继续执行就会发生不可预知的错误,中止执行。
Rust提供了用于可恢复错误的Result<T,E>和用于中止程序执行的panic!宏。
不可恢复错误与panic!
panic!宏会打印一段错误信息,展开并清理调用栈,然后退出程序。
还可以设置立即终止程序,不进行清理工作,内存由操作系统回收。在Cargo.toml下的[profile]区域添加如下内容:
[profile]
panic = 'abort'可以指定在发布模式中使用终止模式
[profile.release]
panic = 'abort'执行cargo run时使用RUST_BACKTRACE=1来显示调用栈信息。这些回溯信息是在调试模式下打印的,因此不要使用--release标志。
可恢复错误与Result
Result枚举的定义如下:
enum Result<T,E> {
Ok(T),
Err(E),
}其中Ok变体包含成功时的值,Err变体是出错时的错误。
打开一个文件,我们需要判断是否打开成功,示例如下:
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => {
// panic!不要求分支的返回值与其它能够正常返回的分支的值一样
panic!("These was a problem opening the file: {:?}", error)
},
};
}还可以对错误类型进行更详细的处理
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Tried to create file but there was a problem: {:?}", e),
},
other_error => panic!("There was a problem opening the file: {:?}", other_error),
},
};
}Result<T,E>通过match表达式实现了许多接收闭包的方法。通过闭包可以将上面的代码改写为,简化嵌套的match表达式:
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt").map_err(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Tried to create file but there was a problem: {:?}", error);
})
} else {
panic!("There was a problem opening file: {:?}", error);
}
});
}失败触发panic的快捷方式:unwrap和expect
当Result的返回值是Ok变体的时候,unwrap返回Ok内部的值。
当Result的返回值是Err变体的时候,unwrap会调用panic!宏。
示例:
use std::fs::File;
fn main() {
let f = File::open("hello.txt").unwrap();
}expect是在unwrap的基础上可以指定panic!附带的错误信息:
use std::fs::File;
fn main() {
let f = File::open("hello.txt").expect("There was a problem opening file");
}传播错误
对于函数内部的可恢复错误,可以将错误返回给调用者,由调用者决定如何处理错误。
use std::io::{self, Read};
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
let 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),
}
}错误传播在Rust中非常常见,所以专门提供了一个?运算符来简化语法。
- 如果Result的返回值是Ok,那么会将Ok中的值作为表达式的返回结果。
- 如果Result的返回值是Err,那么就会将这个值作为结果返回,就像使用了return语句。 上面的代码可以改写为:
use std::io::{self, Read};
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}被?接收的错误会隐式调用from函数(实现了From trait)转为返回值类型的错误。只要实现了能够转为返回类型的from函数,?都可以处理。