错误处理是程序开发中必不可少的一个环节,在Rust中,错误分成两个类别:可恢复错误和不可恢复错误。
可恢复错误:比如说未找到文件,Rust中用Result<T,E>来实现
不可恢复错误:比如数组访问越界,Rust中用panic!实现
1.panic!
这个有感叹号,很显然是一个宏,我们来使用一下子。
fn main() {panic!("panic here!");
}
报错信息:
thread 'main' panicked at 'panic here!', srcmain.rs:2:5
stack backtrace:0: backtrace::backtrace::trace_unsynchronized
...
这底下还有很多调用堆栈的信息哈,我们现在不去关心它
使用RUST_BACKTRACE=1命令来运行程序也可以调出调用堆栈信息。
2.Result
Result其实是一个枚举类型,它的原型如下:
enum Result<T, E> {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)', srcmain.rs:8:23
stack backtrace:0: backtrace::backtrace::trace_unsynchronized
emmm,我这里居然还是中文!。。。我以为是No such a file or directory.
3.简写形式
我们可以通过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: "系统找不到指定的文件。" }', srclibcoreresult.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: "系统找不到指定的文件。" }', srclibcoreresult.rs:1165:5
stack backtrace:0: backtrace::backtrace::trace_unsynchronized
4.传播错误
当我们调用一个函数的时候,函数内部可能出错,而调用方需要捕捉错误信息,因此可以将错误传出来,这个过程就是传播错误。
还是以读取文件内容来举例:
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<String, io::Error> {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)
也是把错误信息打印出来了。
5.小问号,你是否有很多朋友?
我们可以通过“?”,来做到传播错误的简写:
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<String, io::Error> {let mut f = File::open("Rust.txt")?;let mut str = String::new();f.read_to_string(&mut str)?;Ok(str)
}
还可以再简单一点:
fn read_file() -> Result<String, io::Error> {let mut str = String::new();File::open("Rust.txt")?.read_to_string(&mut str)?;Ok(str)
}
就很棒!
6.panic!和Result如何选择?
在测试过程中,我们可以使用panic!,这样调试起来比较方便。
但是在正式发布的代码中,为了程序的健壮性和容错率,最好不要使用panic!
个人观点哈~
7.panic的实现机制
在Rust中,panic的实现方式有两种:unwind和abort
unwind 方式在发生panic 的时候,会一层一层地退出函数调用枝,在此过程中,当前栈内的局部变量还可以正常析构。
abort 方式在发生panic 的时候,会直接退出整个程序。
一般来说,默认情况下,编译器都是使用的unwind模式。
如何用户自己制定:
rustc -C panic=unwind test.rs
rustc -C panic=abort test.rs
欢迎关注公众号:Rust编程之路