写一个能让调用方好好处理的错误类型,需要实现哪些 trait?source 链怎么用?Box
源码:
1 | use std::{error::Error, fmt, num::ParseIntError, fs, io};
2 | #[derive(Debug)]
3 | enum ConfigError { Io(io::Error), Parse { key: String, source: ParseIntError }, Missing(&'static str) }
4 | impl fmt::Display for ConfigError {
5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6 | match self { ConfigError::Io(_) => write!(f, "读取配置文件失败"), ConfigError::Parse { key, .. } => write!(f, "配置项 {} 不是有效整数", key), ConfigError::Missing(k) => write!(f, "缺少配置项 {}", k) }
7 | }
8 | }
9 | impl Error for ConfigError {
10 | fn source(&self) -> Option<&(dyn Error + 'static)> {
11 | match self { ConfigError::Io(e) => Some(e), ConfigError::Parse { source, .. } => Some(source), ConfigError::Missing(_) => None }
12 | }
13 | }
14 | impl From<io::Error> for ConfigError { fn from(e: io::Error) -> Self { ConfigError::Io(e) } }
15 | fn load(path: &str, key: &'static str) -> Result<i32, ConfigError> {
16 | let text = fs::read_to_string(path)?;
17 | let line = text.lines().find(|l| l.starts_with(key)).ok_or(ConfigError::Missing(key))?;
18 | let v = line.split('=').nth(1).unwrap_or("").trim();
19 | v.parse().map_err(|e| ConfigError::Parse { key: key.to_string(), source: e })
20 | }
21 | fn report(e: &dyn Error) { println!("错误: {}", e); let mut cur = e.source(); let mut d = 1; while let Some(s) = cur { println!("{}由 {} 引起", " ".repeat(d), s); cur = s.source(); d += 1; } }
22 | fn main() {
23 | let dir = std::env::temp_dir().join("u73cfg"); fs::create_dir_all(&dir).unwrap();
24 | let p = dir.join("a.conf"); fs::write(&p, "port = 8080\nworkers = many\n").unwrap();
25 | let ps = p.to_str().unwrap();
26 | println!("port -> {:?}", load(ps, "port"));
27 | for (path, key) in [(ps, "workers"), (ps, "timeout"), ("/不存在的/文件", "port")] {
28 | match load(path, key) { Ok(v) => println!("{} -> {}", key, v), Err(e) => { report(&e); println!(" Debug: {:?}", e); } }
29 | }
30 | let _ = fs::remove_dir_all(&dir);
31 | }
编译:成功
运行(退出码 0):
port -> Ok(8080)
错误: 配置项 workers 不是有效整数
由 invalid digit found in string 引起
Debug: Parse { key: "workers", source: ParseIntError { kind: InvalidDigit } }
错误: 缺少配置项 timeout
Debug: Missing("timeout")
错误: 读取配置文件失败
由 No such file or directory (os error 2) 引起
Debug: Io(Os { code: 2, kind: NotFound, message: "No such file or directory" })? 的转换规则见Result 错误传播。
源码:
1 | use std::error::Error;
2 | #[derive(Debug)] struct MyErr;
3 | impl Error for MyErr {}
4 | fn main() { let e: Box<dyn Error> = Box::new(MyErr); println!("{:?}", e); }
编译:失败
error[E0277]: `MyErr` doesn't implement `std::fmt::Display`
--> <实验目录>/src/u73b.rs:3:16
|
3 | impl Error for MyErr {}
| ^^^^^ unsatisfied trait bound
|
help: the trait `std::fmt::Display` is not implemented for `MyErr`
--> <实验目录>/src/u73b.rs:2:18
|
2 | #[derive(Debug)] struct MyErr;
| ^^^^^^^^^^^^
note: required by a bound in `std::error::Error`
--> <rust 标准库源码>/library/core/src/error.rs:59:26
|
59 | pub trait Error: Debug + Display {
| ^^^^^^^ required by this bound in `Error`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`. 编译:成功
运行(退出码 0):
k=0: 是 Quota,内部值 100
k=1: 是 ParseIntError: invalid digit found in string
size_of::<Box<dyn Error>>() = 16 字节(胖指针) 编译:成功
运行(退出码 0):
Small 2 WithString 24 Big 129
Result<u32, Small> 8 Result<u32, WithString> 24 Result<u32, Big> 132 Result<u32, Box<Big>> 16
io::Error 8 ParseIntError 1 Box<dyn Error> 16thiserror 与 anyhow 把这些样板代码自动化,见thiserror 与 anyhow 的边界。