对于程序员来说,错误处理的重要性是不言而喻的,贯穿于代码编写、开发、调试,以及交付运行的全过程中。对于此等重要的工作,Rust 生态中特别有一个 crate error-chain 对错误处理在 Rust 标准库之上给予了支持。
本文我们通过在 main 方法中对错误适当处理、避免在错误转变过程中遗漏错误、获取复杂错误场景的回溯三个方面来了解 crror-chian crate。
在 main 方法中对错误适当处理
处理尝试打开不存在的文件时发生的错误,是通过使用 error-chain crate 来实现的。error-chain crate 包含大量的模板代码,用于 Rust 中的错误处理。
foreign_links 代码块内的 Io(std::io::Error) 函数允许由 std::io::Error 所报错误信息到 error_chain! 所定义错误类型的自动转换,error_chain! 所定义错误类型将实现 Error trait。
下文的实例将通过打开 Unix 文件 /proc/uptime 并解析内容以获得其中第一个数字,从而告诉系统运行了多长时间。除非出现错误,否则返回正常运行时间。
本书中的其他实例将隐藏 error-chain 模板,如果需要查看,可以通过 ⤢ 按钮展开代码。
代码语言:javascript复制use error_chain::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),
};
}
获取复杂错误场景的回溯
本实例展示了如何处理一个复杂的错误场景,并且打印出错误回溯。依赖于 chain_err,通过附加新的错误来扩展错误信息。从而可以展开错误堆栈,这样提供了更好的上下文来理解错误的产生原因。
下述代码尝试将值 256 反序列化为 u8。首先 Serde 产生错误,然后是 csv,最后是用户代码。
代码语言:javascript复制use error_chain::error_chain;
use serde::Deserialize;
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);
}
}
错误回溯信息如下:
代码语言:javascript复制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
因公众号篇幅和体验限制,通过附加新的错误来扩展错误信息等实例请点击底部“阅读原文”,或者访问 https://rust-cookbook.budshome.com,按照左侧导航阅读。
以上实例代码都是完整的、可独立运行的程序,因此你可以直接复制它们到自己的项目中进行试验。
如果希望从头了解如何运行上述实例代码,请参考《Rust 实践指南》中关于本书-如何使用本书实例部分。也可以复制链接:http://budshome.com/rust-cookbook/about.html