我推荐使用本项目中的 exception.rs
. 其已被高频地使用一年, 与其他模块相比, 经受了最多的考验. 每当程序出bug时, exception.rs
能帮我快速地找到报错位置和原因, 比panic/unwrap/expect/assert
好用很多!
如果复制或改进其源码, 请注明出处.
首先, 在你自己的lib.rs
或main.rs
里, 将exception.rs
中的所有符号重新导出 (re-export) 为crate::exception::*
. 必须如此, 两个宏才能被编译. 重新导出的方式有很多, 参考:
/* 在 lib.rs 或 main.rs 里*/
/* pub */ mod exception {
pub use xuanmi_base_support::{self,
Exception, Outcome,
TraitStdResultToOutcome, TraitStdOptionToOutcome
};
pub use xuami_base_support::{throw, assert_throw};
}
或者, 你只想使用exception.rs
, 那就把它放进自己的源码仓库, 参考:
/* 在 lib.rs 或 main.rs 里*/
mod exception; use exception::{self, *};
然后, 在实现任何 可能失败的函数 (fallable function) 时, 参考以下代码片:
use crate::exception::*;
/* async */ fn foo_may_fail(/* params */) -> Outcome</* ret-type */> {
let a = foo_a_may_fail(params)/*.await*/.catch(
"错误标题",
&format!("错误原因. 有助于诊断的变量的值: {}", blabla)
)?; // 别忘了问号.
// 如果你确信 `foo_b_may_fail` 内部写了详细的错误信息, 那就不必再写一遍.
let b = foo_b_may_fail(params)/*.await*/.catch_()?;
let c = get_c(params)/*.await*/.ifnone(
"错误标题",
&format!("错误原因. 有助于诊断的变量的值: {}", blabla)
)?;
let d = get_d(params)/*.await*/.ifnone_()?;
// 建议用 assert_throw! 取代 assert!, assert_eq!
assert_throw!(a > b);
assert_throw!(c > d, "&str类型的错误细节");
assert_throw!(
a+b == c*d,
"&str类型的错误标题",
"&str类型的错误细节"
);
Ok((a, b, c, d))
}
struct Exception
- 实现了
trait fmt::Display
, 使得程序遭遇异常时, 能够打印出类似于Java/Python那样的错误栈. - 具有一个
inner
字段, 用于维护错误栈.
- 实现了
type Outcome
, 是std::Result<T, Box<Exception>>
的别名. 起名为Outcome
是为了避免和Result
撞名.trait TraitStdResultToOutcome
, 给Result<T, E>
挂接了catch(self, name, ctx)
和catch_(self)
两个函数. 调用任何一个函数, 会构造一个Outcome
对象, 对象的T
分支直接移动self
的T
分支, 对象的E
分支把inner
字段设定为self
, 并设定名称、上下文、行号等字段.- 类似地,
trait TraitStdOptionToOutcome
, 给Option<T>
挂接了ifnone(self, name, ctx)
和ifnone_()
两个函数. - 宏
throw!(name, ctx)
, 以及宏assert_throw!(bool_expr, [[name], ctx])
, 构造一个没有内部错误的Exception
对象, 并使宏的调用者返回该错误对象.