panic!
默认panic!时,rust会unwinding,回溯stack并且清理剩余数据。如果需要最快速度停止而不清理,就可以在Cargo.toml这样设置:
[profile.release]# 在release模式下 panic = 'abort'
panic之后会返回错误信息:
$ cargo run Compiling panic v0.1.0 (file:///projects/panic) Finished dev [unoptimized + debuginfo] target(s) in 0.27s Running `target/debug/panic` thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libcore/slice/mod.rs:2806:10 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
Result
enum Result<T, E> { Ok(T), Err(E), }
粗略处理:
use std::fs::File; fn main() { let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => panic!("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("hello.txt") { Ok(fc) => fc, Err(e) => panic!("Problem creating the file: {:?}", e), }, other_error => {//注意other_error panic!("Problem opening the file: {:?}", other_error) } }, }; }
use std::fs::File; use std::io::ErrorKind; fn main() { let f = File::open("hello.txt").unwrap_or_else(|error| { if error.kind() == ErrorKind::NotFound { File::create("hello.txt").unwrap_or_else(|error| { panic!("Problem creating the file: {:?}", error); }) } else { panic!("Problem opening the file: {:?}", error); } }); }
unwrap
unwrap:如果Result是Ok,就取出结果,否则,就panic!
use std::fs::File; fn main() { let f = File::open("hello.txt").unwrap(); }
expect
expect: 类似unwrap,但是允许选择panic的信息(替换掉了出错的statement本身)
use std::fs::File; fn main() { let f = File::open("hello.txt").expect("Failed to open hello.txt"); }
在设计原型的时候,使用expect和unwrap都能在不损伤可读性和流畅度的同时为日后细致处理错误留下一定前期准备。
传递错误,?操作符
鼓励可能出错的函数传递潜在的错误。
use std::fs::File; use std::io; use std::io::Read; 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_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), } }
以上逻辑可以用?操作符来进行缩减-如果正常,就继续后面的逻辑,否则直接返回错误。
use std::fs::File; use std::io; use std::io::Read; fn read_username_from_file() -> Result<String, io::Error> { let mut s = String::new(); File::open("hello.txt")?.read_to_string(&mut s)?; Ok(s) }
需要注意的是?操作符只能在返回Result<T,E>的函数中使用。也就是说,如果一个本身没有返回值的函数使用?操作符返回错误,只需要返回Result<(), Error>即可。
use std::error::Error; use std::fs::File; fn main() -> Result<(), Box<dyn Error>> { let f = File::open("hello.txt")?; Ok(()) }
何时使用panic,何时对错误进行重抛等处理
当函数在逻辑上应该只返回Ok这种情况的时候,可以直接使用unwrap,即出错就panic。
当不熟悉的第三方调用你的库并且传入错误的值时,可以panic,这可以提醒程序员迅速修复这个问题。同理,当你使用不熟悉的第三方库发生了无法修复的错误时,也应该panic,以和对方沟通或者替换这个第三方库。
当错误可以预知时,返回Result而不是panic!会更好。例如收到了不合规范的http请求。
当代码对数值进行验证时,处理不当的数据可能会导致信息泄露,因此文中建议,也仅仅建议,直接panic。文中认为函数本身就是在一定的输入输出规范下才能保证安全和有用,如果这个输入规范被调用者弄错了,就应该让调用者自己负责改好。剩下的都是在夸Rust。