《Rust权威指南》学习笔记之第9章 错误处理
- 不可恢复错误与panic!
- panic!中产生回溯信息
- 可恢复错误与Result
- 匹配不同错误
- 失败时触发panic的快捷方式:unwrap和expect
- 传播错误
- 传播错误的快捷方式:?运算符
- ?运算符只能被用于返回Result的函数
- 要不要使用panic!
- 创建自定义类型进行有效性验证
两大类错误:
- 可恢复错误
文件未找到等,报告给用户并再次尝试。Result<T, E> - 不可恢复错误
bug,尝试访问超过数组结尾的位置等。panic!
不可恢复错误与panic!
panic!宏执行时打印出一段错误提示信息,展开并清理当前的调用栈,然后退出程序。
panic中的栈展开与终止
panic发生时,程序会默认开始栈展开,反向遍历所有调用函数,并清理函数中的错误,需要二进制中存储许多额外信息。可以立即终止程序,而不进行任何清理,使用内存由操作系统回收。通过配置Carog.toml文件,减小二进制包。
[profile.release]
panic = ‘abort’
fn main() {
panic!("crash and burn");
}
panic!中产生回溯信息
fn main() {
let v = vec![1, 2, 3];
v[99]; //缓冲区溢出(buffer overread)
}
获得回溯信息,必须启用调试符号 (debug symbol),cargo build或cargo run不附带–release标志。
RUST_BACKTRACE=1 cargo run
可恢复错误与Result
enum Result<T, E> {
OK(T),
Err(E),
}
use std::fs::file;
fn main() {
let f = File::open("hello.txt");
//let f: u32 = File::open("hello.txt"); //报错查看类型
}
use std::fs::file;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => {
panic!("There 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("hell.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),
},
}
}
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("hell.txt").unwrap_or_else(|error| {
panic!("Tried to create file but there was a problem: {:?}", error);
});
} else {
panic!("There was a problem opening the file: {:?}", other_error);
}
});
}
失败时触发panic的快捷方式:unwrap和expect
unwrap方法,Result返回值是Ok时,返回Ok内值;Error时,调用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("Failed to open hello.txt");
}
传播错误
函数中包含可能会执行失败的调用时,可以将错误返回给调用者。
use std::io;
use std::io::Read;
use std::fs::File;
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),
}
let mut s = String::new();
match f.read_to_sttring(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
传播错误的快捷方式:?运算符
?放于Result后,Result值是Ok时,返回Ok中的值并继续执行程序;Err时,返回并退出程序。
?运算符接收的错误值会隐式地被from函数处理,定义于标准库的From trait中,用于错误类型间转换(传入错误类型转换为当前函数返回错误类型)。
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_sttring(&mut s)?;
Ok(s)
}
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_sttring(&mut s)?;
Ok(s)
}
use std::io;
use std::fs;
fn read_username_from_file() -> Result<String, io::Error> {
fs.read_to_sttring("hello.txt")
}
?运算符只能被用于返回Result的函数
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("hello.txt")?;
Ok(())
}
要不要使用panic!
不可恢复用panic!,定义可能失败的函数(失败可预期)优先考虑使用Result。
示例、原型和测试使用panic!。
use std::net::IpAddr;
let home: IpAddr = "127.0.0.1".parse().unwrap();
创建自定义类型进行有效性验证
loop {
let guess: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
if guess<1 || guess>100 {
println!("The secret number will be between 1 and 100.");
continue;
}
match guess.cmp(&secret_number) {
}
}
pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value<1 || value>100 {
panic!("Guess number must be between 1 and 100, got {}.", value);
}
}
pub fn value(&self) -> i32 { //读取接口(getter)
self.value
}
}