rust 初探 – 错误处理
- 可恢复的错误:文件未找到,重试(Result<T,E>)
- 不可恢复的错误:访问索引超出范围(panic!)
panic! 不可恢复错误
当 panic! 执行的时候:
-
会打印一个错误信息
-
展开、清理调用栈(默认;可以立即终止,不进行清理,设置 panic=‘abort’)
-
退出程序
-
可以使用 set RUST_BACKTRACE=0 打印调用栈
-
为获取带有调试信息的回溯,必须启用调试符号(不带 – release)
Result 枚举与可恢复的错误
Result 枚举
enum Result<T,E>{
Ok(T), // 操作成功
Err(E),// 操作失败
}
use std::{collections::HashMap, fs::File, io::ErrorKind};
fn main() {
let f = File::open("hello.txt");
match f {
Ok(file) => file,
// Err(error) => {
// panic!("file open fail with err: {:?}", error)
// }
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("creat err: {:?}", e),
},
other_error => panic!("file open fail with err: {:?}", error),
},
};
}
unwrap
- unwrap: match 表达式的一个快捷方法:
- 如果 Result 结果是 Ok,返回 Ok 里面的值
- 如果 Result 结果是 Err,调用 panic!
use std::{collections::HashMap, fs::File, io::ErrorKind};
fn main() {
// 下面的两部份代码是等效的
// let f = File::open("hello.txt");
// match f {
// Ok(file) => file,
// Err(error) => {
// panic!("file open fail with err: {:?}", error)
// }
// };
let f = File::open("hello.txt").unwrap();
let f = File::open("hello.txt").expect("这里可以自定义错误信息");
}
传播错误
定义:将错误返回出去,给调用者处理
use std::{fs::File, io::{self, Read}};
fn read_data_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),
}
}
fn main() {
let res = read_data_from_file();
println!("res: {:?}", res)
}
可以使用 ? 运算符进行简化
fn read_data_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?;
// 等同于下面的逻辑
// let mut f = match f {
// Ok(file) => file,
// Err(e) => return Err(e),
// };
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
// 等同于下面的逻辑
// match f.read_to_string(&mut s) {
// Ok(_) => Ok(s),
// Err(e) => Err(e),
// }
}
- 如果 Result 是 Ok:Ok 中的值就是表达式的结果,然后继续执行程序
- 如果 Result 是 Err:Err 就是整个函数的返回值,就像使用了 return
? 和 from 函数
被 ? 所应用的错误,会隐式地被 from 函数处理
- 当 ? 调用 from 函数时:它所接收的错误类型会被转化成为当前函数返回类型所定义的错误类型
- 作用:针对不同错误原因,返回同一种错误类型
- 只要每个错误类型实现了转换为所返回的错误类型的 from 函数
? 和链式调用
上面的示例还可以进一步简化:
fn read_data_from_file() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?read_to_string(&mut s)?;
Ok(s)
}
- ? 运算符只能用于返回结果是 Result 的函数
- main 函数的返回类型也可以是 Result 类型
fn main() -> Result<(), Box<dyn Error>> {
...
}
什么时候应该使用 panic
- 在定义一个可能失败的函数时,优先考虑返回 Result
- 否则就 panic!