错误处理
main 中的正确处理错误
要处理尝试打开不存在的文件时,发生的错误。我们会用到error-chain,这是一个大量样板代码的库,用来[处理 Rust 的错误]。
foreign_links
里面的Io(std::io::Error)
,允许从std::io::Error
到error_chain!
所定义类型的自动转换,这些类型会实现Error
trait。
下面的食谱是,在打开 unix 文件/proc/uptime
,然后分析内容得到第一个数字期间,告诉我们系统运行了多长时间。要返回 uptime,除非出现错误。
本书中的其他食谱,会隐藏error-chain样板文件,可以通过按钮(展开代码)来查看。
#[macro_use] extern crate error_chain; use std::fs::File; use std::io::Read; error_chain!{ foreign_links { Io(std::io::Error); ParseInt(::std::num::ParseIntError); } } fn read_uptime() -> Result<u64> { let mut uptime = String::new(); File::open("/proc/uptime")?.read_to_string(&mut uptime)?; Ok(uptime .split('.') .next() .ok_or("Cannot parse uptime data")? .parse()?) } fn main() { match read_uptime() { Ok(uptime) => println!("uptime: {} seconds", uptime), Err(err) => eprintln!("error: {}", err), }; }
避免在错误转换期间,丢掉了错误
这个error-chain箱子,能匹配函数返回的不同错误类型,可能是相对紧凑的。ErrorKind
能确定错误类型。
使用reqwest查询一个随机整数生成器的 Web 服务。将响应的字符串,转换为整数。我们有 Rust 标准库,reqwest,并且 Web 服务的全部错误都会(可能)发生。要明确定义的 Rust 错误,请使用foreign_links
。 对于额外的 Web 服务错误ErrorKind
变种,使用error_chain!
宏的errors
代码块。
#[macro_use] extern crate error_chain; extern crate reqwest; use std::io::Read; error_chain! { foreign_links { Io(std::io::Error); Reqwest(reqwest::Error); ParseIntError(std::num::ParseIntError); } errors { RandomResponseError(t: String) } } fn parse_response(mut response: reqwest::Response) -> Result<u32> { let mut body = String::new(); response.read_to_string(&mut body)?; body.pop(); body.parse::<u32>() .chain_err(|| ErrorKind::RandomResponseError(body)) } fn run() -> Result<()> { let url = format!("https://www.random.org/integers/?num=1&min=0&max=10&col=1&base=10&format=plain"); let response = reqwest::get(&url)?; let random_value: u32 = parse_response(response)?; println!("a random number between 0 and 10: {}", random_value); Ok(()) } fn main() { if let Err(error) = run() { match *error.kind() { ErrorKind::Io(_) => println!("Standard IO error: {:?}", error), ErrorKind::Reqwest(_) => println!("Reqwest error: {:?}", error), ErrorKind::ParseIntError(_) => println!("Standard parse int error: {:?}", error), ErrorKind::RandomResponseError(_) => println!("User defined error: {:?}", error), _ => println!("Other error: {:?}", error), } } }
获取复杂的错误场景的回溯
这个食谱,演示了如何处理复杂的错误场景,然后打印回溯。它依赖chain_err
附加新错误,来扩展错误(信息)。错误(信息)栈可以展开,从而提供更好的上下文,来理解引发错误的原因。
以下食谱,尝试将值256
反序列化成一个u8
。一个错误从 serde 开始冒泡,然后是 csv,最后到达,用户代码。
# extern crate csv; #[macro_use] extern crate error_chain; # #[macro_use] # extern crate serde_derive; # # use std::fmt; # # error_chain! { # foreign_links { # Reader(csv::Error); # } # } #[derive(Debug, Deserialize)] struct Rgb { red: u8, blue: u8, green: u8, } impl Rgb { fn from_reader(csv_data: &[u8]) -> Result<Rgb> { let color: Rgb = csv::Reader::from_reader(csv_data) .deserialize() .nth(0) .ok_or("Cannot deserialize the first CSV record")? .chain_err(|| "Cannot deserialize RGB color")?; Ok(color) } } # impl fmt::UpperHex for Rgb { # fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { # let hexa = u32::from(self.red) << 16 | u32::from(self.blue) << 8 | u32::from(self.green); # write!(f, "{:X}", hexa) # } # } # fn run() -> Result<()> { let csv = "red,blue,green 102,256,204"; let rgb = Rgb::from_reader(csv.as_bytes()).chain_err(|| "Cannot read CSV data")?; println!("{:?} to hexadecimal #{:X}", rgb, rgb); Ok(()) } fn main() { if let Err(ref errors) = run() { eprintln!("Error level - description"); errors .iter() .enumerate() .for_each(|(index, error)| eprintln!("└> {} - {}", index, error)); if let Some(backtrace) = errors.backtrace() { eprintln!("{:?}", backtrace); } # # // In a real use case, errors should handled. For example: # // ::std::process::exit(1); } }
已呈现的错误回溯:
Error level - description
└> 0 - Cannot read CSV data
└> 1 - Cannot deserialize RGB color
└> 2 - CSV deserialize error: record 1 (line: 2, byte: 15): field 1: number too large to fit in target type
└> 3 - field 1: number too large to fit in target type
使用RUST_BACKTRACE=1
,显示与此错误关联的backtrace
的详细信息。