Rust 学习 09
archive time: 2022-05-23
今天来看看错误处理
错误处理
按照是否可以恢复, Rust 里的错误可以分为 可恢复 和 不可恢复 两种
对应于 Option<T>
, Result<T, E>
和 panic!
等
panic!
panic!
应该算是比较常见的错误了, 而且是不可恢复的错误, 数组越界什么的就会引起 panic!
如果遇到 panic!
宏:
- 你的程序会打印一个错误信息
- 展开 (unwind) 和 清理调用栈 (Stack)
- 退出程序
当然, 我们可以在 [profile]
里设置展开或是中止 (abort) 调用栈
默认情况会展开调用栈, 清理使用内存, 工作量很大
但是如果不展开, 使用的内存需要操作系统来清理
# Cargo.toml
# ...
[profile.release]
panic = "abort"
如果想要更加详细的回溯信息, 可以通过设置 RUST_BACKTRACR
变量来实现
powershell 下使用
$env:RUST_BACKTRACE=1
来启用
Result<T, E>
不是所有的错误都需要中断程序来解决, 对于其中的一些错误, 我们可以使用 Result<T, E>
这个枚举类型来处理
enum Result<T, E> {
Ok(T),
Err(E),
}
T 对应操作成功的返回值, E 对应错误的类型, 可以使用 match
来区分
// 尝试打开文件, 打不开就尝试创建
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!("Error creating file: {:#?}", e),
},
oe => panic!("Error opening the file: {:#?}", oe),
},
};
println!("{:#?}", f);
}
/*
File {
handle: 0x00000000000000f0,
path: "\\?\\C:\\Users\\kands\\Documents\\projs\\Drafts\rust\\learn_rust\\hello.txt",
}
*/
对于上述代码, 我们可以使用 Closure 来改良
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt").unwrap_or_else(|err| {
if err.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Error creating file: {:#?}", error);
})
} else {
panic!("Error opening the file: {:#?}", err);
}
});
println!("{:#?}", f);
}
不过具体使用哪种风格是取决于你的习惯
处理 Result<T, E>
还有 unwrap()
和 expect()
两个方法
第一个方法默认会成功, 第二个是可以自定义错误信息, 成功都会返回正确内容
传播错误
传播错误, 也就是返回 Option<T>
或者 Result<T, E>
类型, 来让调用者知道和处理错误
在 Rust 里, 还可以使用 ?
操作符来实现
fn read_file_by_name(name: &str) -> Result<String, std::io::Error> {
let mut f = File::open(name)?; // if error, return Err
let mut s = String::new();
f.read_to_string(&mut s)?; // same
Ok(s)
}
但是这里需要注意匹配错误类型, 如果错误类型不匹配, Rust 会调用相应错误下的 from()
函数来转化
Trait
std::convert::From
的from()
函数
由于 ?
会返回正确执行时的值, 所以我们还可以进一步简化
fn read_file_by_name(name: &str) -> Result<String, std::io::Error> {
let mut s = String::new();
File::open(name)?.read_to_string(&mut s)?;
Ok(s)
}
但是注意 ?
只能作用于返回值为 Option 或 Result 类型的函数,
或者实现了 Trait Try
的类型, 因为 ?
会调用 from_error
函数
Box<dyn Error>
表示所有可能的错误类型
何时使用
在演示程序或者一些原型代码, 测试代码时可以使用 panic!()
或者 unwrap()
, expect()
等方法来发送 panic
而日常代码, 如果代码可能会 破坏整个程序 或 不是预期情况, 那么建议使用 panic!()
,
否则最好使用 Result<T, E>
或者 Option<T>
好, 今天就学到这里罢, 刷 Codewars 去了
泛型那部分就摸了, 很简单的看看文档就懂了