0.1.0 |
|
---|
#73 在 #result
22KB
329 代码行
Errable
Errable
是一个具有逆 Try
语义的 Option
。
这意味着在 ?
运算符作用于 Errable<E>
时,如果包含错误 E
,则会提前退出;如果值是 Success
,则作为无操作执行。
这与 Option
相比,在 None
值上使用 ?
会提前退出。
Errable
填补了 Result
和 Option
类型留下的空白
可能的成功 | 可能失败 |
---|---|
结果<T |
,E> |
选项<T> |
Errable<E> |
示例
以下代码演示了如何使用 Errable
编写简洁的验证代码,在失败时提前退出。
use errable::Errable::{self, Fail, Success};
// Validates the input number `n`, returning a `Fail`
// if the input number is zero, or `Success` otherwise.
fn fails_if_number_is_zero(n: u32) -> Errable<&'static str> {
if n == 0 {
Fail("number is zero")
} else {
Success
}
};
// Check many numbers, returning early if a tested
// number is equal to zero.
fn check_many_numbers() -> Errable<&'static str> {
fails_if_number_is_zero(1)?;
fails_if_number_is_zero(3)?;
fails_if_number_is_zero(0)?; // <--- Will cause early exit
// Following lines are never reached
fails_if_number_is_zero(10)?;
Success
}
assert_eq!(check_many_numbers(), Errable::Fail("number is zero"));
动机
Errable
填补了 Option
和 Result
留下的空白,并清楚地传达了函数的意图和潜在结果。
返回 Errable
的函数只有两种潜在结果,它可以失败并带有错误 E
,或者它可以成功。
为什么不使用 Result
呢?
因为 Result
暗示了输出。以 std::fs::rename
为例
如果我说 rename
的返回类型是 Result<T, E>
,你猜 T
和 E
会是什么?
你可能会合理地假设 E
是 std::io::Error
,但 T
呢?它可以合理地返回任何数量的事物
- 重命名文件目的地的规范路径。
- 移动文件的大小。
- 被重命名文件替换的文件的大小(如果有)。
- 或者甚至可能是被覆盖文件的句柄。
当然,这些都不正确,因为T
的值是单位值()
。函数rename
永远不会产生任何输出,只能信号错误。那么为什么不明确地通知用户呢?
我认为使用一个可以表示失败潜力的类型,但在成功时没有输出,可以更清楚地表达使用此函数时的意图和潜在结果。
为什么不使用Option
呢?
潜在的失败可以使用Option<E>
来表示,但如上所述,Option
的
type Error = &'static str;
fn fails_if_number_is_zero(n: u32) -> Option<Error> {
if n == 0 {
Some("number is zero")
} else {
None
}
};
fn check_many_numbers() -> Option<Error> {
// We have to explicitly check, since using `?` here would result in an early exit,
// if the call returned None, which is the opposite of what we intend.
if let Some(err) = fails_if_number_is_zero(1) {
return Some(err)
}
// .. Repeating the above three lines for each check is tedious compared to
// just using the `?` operator, as in the example.
None
}
从Result
转换
从使用Result
切换到Errable
非常简单,以下是一个前后示例来展示这一点。
fn validate_number(x: u32) -> Result<(), &'static str> {
match x {
0 ..= 9 => Err("number is too small"),
10..=30 => Ok(()),
31.. => Err("number is too large")
}
}
使用Errable
fn validate_number(x: u32) -> Errable<&'static str> {
match x {
0 ..= 9 => Fail("number is too small"),
10..=30 => Success,
31.. => Fail("number is too large")
}
}
兼容性
Errable
包含将到和从Result
和Option
进行映射的实用函数,以及FromResidual
实现,当与?
运算符一起使用时,可以自动执行这些转换。
fn fails_if_true(should_fail: bool) -> Errable<&'static str> {
if should_fail {
Fail("Darn it!")
} else {
Success
}
}
fn try_producing_value() -> Result<u32, &'static str> {
fails_if_true(false)?;
fails_if_true(true)?;
Ok(10)
}