错误分类:
可恢复错误:Result<T,E>。文件查找不存在,可以重试。
不可恢复错误:panic!()宏。访问数组越界,会发生panic
不可恢复错误:
处理方式:
栈展开:Rust会沿着调用栈的反向顺序遍历所有调用函数,并依次清理这些函数中的数据。
立即终止:直接结束程序且不进行任何清理工作,程序所使用过的内存只能由操作系统来进行回收。
Cargo.toml文件中添加配置:
[profile.release]
panic = 'abort'
在最终生成包里会采用立即终止程序
获取回溯信息:
回溯信息可能包含Rust核心库、标准库和第三方库
RUST_BACKTRACE=1 cargo run
可恢复的错误:
num Result<T, E> {
Ok(T),
Err(E),
}
T代表了Ok变体中包含的值类型;E则代表了Err变体中包含的错误类型
Result枚举及其变体已经通过预导入模块被自动地引入当前作用域中
可以根据不同的错误原因,做出不同的处理:
use std::fs::File;
use std::io::ErrorKind;
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() { // error.kind() 返回的是枚举类型;该枚举类型需要引入标准库的ErrorKind
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), // 这里使用了other_error,表示不再穷举,对剩余的情况的统一处理
},
};
File::open返回的Err变体中的错误值类型,是定义在某个标准库中的结构体类型:io::Error。这个结构体拥有一个被称作kind的方法,调用它来获得io::ErrorKind值。
ErrorKind枚举:
enum ErrorKind {
NotFound,
PermissionDenied,
DiskFull,
}
利用闭包简化match嵌套:
let f = File::open("hello.txt").map_err(|error| { // 这里调用map_err()函数,里面是闭包函数
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| { // 这里调用unwrap_or_else()函数,里面是闭包
panic!("Tried to create file but there was a problem: {:?}", error);
})
} else {
panic!("There was a problem opening the file: {:?}", error);
}
});
这里使用了两个函数map_err()和unwrap_or_else():
map_err():
map_err 方法用于将错误类型从一个类型转换为另一个类型。它接收一个闭包(closure),该闭包将原始错误类型转换为新的错误类型。map_err 通常用于在错误处理链中将错误类型统一化,以便更方便地处理错误。
该代码中error.kind() 将io::Error转换成io::ErrorKind, 然后进行比较。
use std::fs::File;
use std::io::Error;
let file = File::open("non_existent_file.txt").map_err(|err| {
Error::new(err.kind(), "文件不存在")
});
使用 map_err 将 io::Error 转换为自定义的 Error 类型。
unwrap_or_else():
unwrap_or_else 方法用于处理 Option 或 Result 类型的值。如果值是 Some 或 Ok,则返回其中的值;否则,执行提供的闭包,并返回闭包的返回值。
let value = Some(42);
let result = value.unwrap_or_else(|| 0); // 成功返回42,失败返回0
println!("result: {}", result); // 输出:result: 42
let none_value: Option<i32> = None;
let result = none_value.unwrap_or_else(|| 0);
println!("result: {}", result); // 输出:result: 0
快速触发panic的两个方法:(中断程序继续运行)
unwrap(): 当Result的返回值是Ok变体时,unwrap就会返回Ok内部的值。而当Result的返回值是Err变体时,unwrap则会替我们调用panic! 宏
expect(): 基于unwrap函数,expect("xxx") 可以传入参数,表明错误的具体原因
错误传播:return or ?
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), // 通过return返回Err(e) ,将错误返回给调用者
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e), // 返回Err,返回给调用者。由于这里是函数的最后一个表达式,所以我们不再需要显式地添加return。
}
}
?运算符:
传播错误的模式在Rust编程中非常常见,所以Rust专门提供了一个问号运算符(?)来简化它的语法。
将?放置于Result值。假如这个Result的值是Ok,那么包含在Ok中的值就会作为这个表达式的结果返回并继续执行程序。假如值是Err,那么这个值就会作为整个程序的结果返回,如同使用了return一样将错误传播给调用者。
使用了?运算符的函数必须返回Result、Option或任何实现了std::ops::Try的类型
fn read_username_from_file() -> Result<String, io::Error> { // 返回类型是Result<String,io::Error>
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?; //这里是加分号
Ok(s)
}
错误类型:
std::io::Error: 表示I/O错误的类型。它包含一个ErrorKind和一个可选的错误消息,用于表示I/O操作过程中的错误,例如文件I/O,网络I/O或其他类型的I/O。
std::io::ErrorKind: 它是一个枚举,表示I/O错误的类型。它的枚举变体有NotFound,PermissionDenied,ConnectionRefused和其他类型。ErrorKind用于对I/O错误的类型进行分类,使其更容易处理和响应不同类型的错误。
std::error::Error: 表示一个泛型的错误类型的trait。它提供一种将错误类型转换为字符串的方法,从而可以向用户显示错误消息。Error是io::Error的超trait。意味着,上线了io::Error也实现了Error。
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> { // 允许将错误转换成字符串显示给用户
let file = File::open("non_existent_file.txt")?;
Ok(())
}
何时panic!:
代码一旦发生panic,就再也没有恢复的可能了.
返回一个Result值时,你就将这种选择权交给了调用者
通过人工检查确保代码永远不会出现Err变体,那就使用unwrap。unwrap内部是调用panic!()宏。
某个错误可能会导致代码处于损坏状态时,使用panic来处理错误。损坏状态意味着设计中的一些假设、保证、约定或不可变性出现了被打破的情形。比如,当某些非法的值、自相矛盾的值或不存在的值被传入代码中
假如错误是可预期的,那么就应该返回一个Result而不是调用panic!