1. 处理错误变量
需要安装error-chain
库,可通过cargo add error-chain
命令安装
[dependencies] error-chain = "0.12.4"
1.1 在 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
并解析内容以获得其中第一个数字,从而告诉系统运行了多长时间。除非出现错误,否则返回正常运行时间。
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 ("无法解析正常运行时间数据" )? .parse ()?) } fn main () { match read_uptime () { Ok (uptime) => println! ("正常运行时间: {} 秒" , uptime), Err (err) => eprintln!("错误: {}" , err), }; }
1.2 避免在错误转变过程中遗漏错误
需要安装reqwest
库,可通过cargo add reqwest --features blocking
命令安装
[dependencies] error-chain = "0.12.4" reqwest = { version = "0.11.16" , features = ["blocking" ] }
error-chain
crate 使得匹配函数返回的不同错误类型成为可能,并且相对简洁。ErrorKind
是枚举类型,可以确定错误类型。下文实例使用 reqwest::blocking
来查询一个随机整数生成器的 web 服务,并将服务器响应的字符串转换为整数。Rust 标准库 reqwest
和 web 服务都可能会产生错误,所以使用 foreign_links
定义易于辨认理解的 Rust 错误。另外,用于 web 服务错误信息的 ErrorKind
变量,使用 error_chain!
宏的 errors
代码块定义。
use error_chain::error_chain;error_chain! { foreign_links { Io (std::io::Error); Reqwest (reqwest::Error); ParseIntError (std::num::ParseIntError); } errors { RandomResponseError (t: String ) } } fn parse_response (response: reqwest::blocking::Response) -> Result <u32 > { let mut body = response.text ()?; 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::blocking::get (&url)?; let random_value : u32 = parse_response (response)?; println! ("0 到 10 之间的随机数: {}" , random_value); Ok (()) } fn main () { if let Err (error) = run () { match *error.kind () { ErrorKind::Io (_) => println! ("标准 IO 错误: {:?}" , error), ErrorKind::Reqwest (_) => println! ("reqwest 请求错误: {:?}" , error), ErrorKind::ParseIntError (_) => println! ("标准解析 int 错误: {:?}" , error), ErrorKind::RandomResponseError (_) => println! ("用户定义错误: {:?}" , error), _ => println! ("其他错误: {:?}" , error), } } }
1.3 获取复杂错误场景的回溯
需要安装csv
和serde
库,可通过cargo add csv
和cargo add serde --features derive
命令安装
[dependencies] error-chain = "0.12.4" csv = "1.2.1" serde = { version = "1.0.160" , features = ["derive" ] }
本实例展示了如何处理一个复杂的错误场景,并且打印出错误回溯。依赖于 chain_err
,通过附加新的错误来扩展错误信息。从而可以展开错误堆栈,这样提供了更好的上下文来理解错误的产生原因。下述代码尝试将值 256
反序列化为 u8
。首先 Serde
产生错误,然后是 csv
,最后是用户代码。
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 ("无法反序列化第一个 CSV 记录" )? .chain_err (|| "无法反序列化 RGB 颜色" )?; 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 (|| "无法读取 CSV 数据" )?; println! ("{:?} 十六进制 #{:X}" , rgb, rgb); Ok (()) } fn main () { if let Err (ref errors) = run () { eprintln!("错误级别 - 描述" ); errors .iter () .enumerate () .for_each (|(index, error)| eprintln!("└> {} - {}" , index, error)); if let Some (backtrace) = errors.backtrace () { eprintln!("{:?}" , backtrace); } std::process::exit (1 ); } }
错误级别 - 描述 └> 0 - 无法读取 CSV 数据 └> 1 - 无法反序列化 RGB 颜色 └> 2 - CSV deserialize error: record 1 (line: 2, byte: 15): field 1: number too large to fit in target type