错误处理是程序开发中必不可少的一个环节,在Rust中,错误分成两个类别:可恢复错误和不可恢复错误。
可恢复错误:比如说未找到文件,Rust中用Result
不可恢复错误:比如数组访问越界,Rust中用panic!实现
这个有感叹号,很显然是一个宏,我们来使用一下子。
fn main() {
panic!("panic here!");
}
报错信息:
thread 'main' panicked at 'panic here!', src\main.rs:2:5
stack backtrace:
0: backtrace::backtrace::trace_unsynchronized
...
这底下还有很多调用堆栈的信息哈,我们现在不去关心它
使用RUST_BACKTRACE=1命令来运行程序也可以调出调用堆栈信息。
Result其实是一个枚举类型,它的原型如下:
enum Result {
Ok(T),
Err(E),
}
我们通过打开文件来举例说明:
use std::fs::File;
fn main() {
let f = File::open("Rust.txt");
//枚举当然需要来match一下
let f = match f {
Ok(file) => file,
Err(error) => panic!("error: {}", error),
};
}
我们的当前目录里并没有这个文件,预期是会出错的。
报错信息:
thread 'main' panicked at 'error: 系统找不到指定的文件。 (os error 2)', src\main.rs:8:23
stack backtrace:
0: backtrace::backtrace::trace_unsynchronized
emmm,我这里居然还是中文!。。。我以为是No such a file or directory.
我们可以通过unwrap或者expect函数来简写程序。
use std::fs::File;
fn main() {
// let f = File::open("Rust.txt");
// //枚举当然需要来match一下
// let f = match f {
// Ok(file) => file,
// Err(error) => panic!("error: {}", error),
// };
let f = File::open("Rust.txt").unwrap();
}
报错信息:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "系统找不到指定的文件。" }', src\libcore\result.rs:1165:5
stack backtrace:
0: backtrace::backtrace::trace_unsynchronized
use std::fs::File;
fn main() {
let f = File::open("Rust.txt").expect("failed to open Rust.txt");
}
报错信息:
thread 'main' panicked at 'failed to open Rust.txt: Os { code: 2, kind: NotFound, message: "系统找不到指定的文件。" }', src\libcore\result.rs:1165:5
stack backtrace:
0: backtrace::backtrace::trace_unsynchronized
当我们调用一个函数的时候,函数内部可能出错,而调用方需要捕捉错误信息,因此可以将错误传出来,这个过程就是传播错误。
还是以读取文件内容来举例:
use std::fs::File;
use std::io::Read;
use std::io;
fn main() {
let res = read_file();
match res {
Ok(str) => println!("str: {}", str),
Err(error) => println!("error: {}", error)
};
}
fn read_file() -> Result {
let f = File::open("Rust.txt");
let mut f = match f {
Ok(file) => file,
Err(error) => return Err(error)
};
let mut str = String::new();
match f.read_to_string(&mut str) {
Ok(_) => Ok(str),
Err(error) => Err(error)
}
}
我们创建了一个Rust.txt文件,运行结果如下:
str: Rust编程之路
我们把文件删除:
运行结果:
error: 系统找不到指定的文件。 (os error 2)
也是把错误信息打印出来了。
我们可以通过“?”,来做到传播错误的简写:
use std::fs::File;
use std::io::Read;
use std::io;
fn main() {
let res = read_file();
match res {
Ok(str) => println!("str: {}", str),
Err(error) => println!("error: {}", error)
};
}
fn read_file() -> Result {
let mut f = File::open("Rust.txt")?;
let mut str = String::new();
f.read_to_string(&mut str)?;
Ok(str)
}
还可以再简单一点:
fn read_file() -> Result {
let mut str = String::new();
File::open("Rust.txt")?.read_to_string(&mut str)?;
Ok(str)
}
就很棒!
在测试过程中,我们可以使用panic!,这样调试起来比较方便。
但是在正式发布的代码中,为了程序的健壮性和容错率,最好不要使用panic!
个人观点哈~
在Rust中,panic的实现方式有两种:unwind和abort
unwind 方式在发生panic 的时候,会一层一层地退出函数调用枝,在此过程中,当前栈内的局部变量还可以正常析构。
abort 方式在发生panic 的时候,会直接退出整个程序。
一般来说,默认情况下,编译器都是使用的unwind模式。
如何用户自己制定:
rustc -C panic=unwind test.rs
rustc -C panic=abort test.rs