4个版本
0.1.3 | 2023年1月12日 |
---|---|
0.1.2 | 2023年1月12日 |
0.1.1 | 2023年1月12日 |
0.1.0 | 2023年1月12日 |
#2026 在 Rust模式
每月下载量 31
27KB
384 行
Fallible
Fallible
是一个具有反转 Try
-语义的 Option
。
这意味着在对一个 Fallible<E>
使用 ?
运算符时,如果包含错误 E
将会提前退出,或者如果值是 Success
则不进行任何操作。
这与在 Option
中使用 ?
运算符在 None
值上将会提前退出形成对比。
Fallible
填补了 Result
和 Option
类型留下的空白
可能的成功 | 可能失败 |
---|---|
结果<T |
,E> |
Option<T> |
Fallible<E> |
示例
此代码演示了如何使用 Fallible
来编写简洁的验证代码,在失败时提前退出。
use fallible_option::Fallible::{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) -> Fallible<&'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() -> Fallible<&'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(), Fallible::Fail("number is zero"));
动机
Fallible
填补了 Option
和 Result
留下的空白,并清楚地传达了函数的意图和潜在结果。
返回 Fallible
的函数只有两种可能的结果,它可能因为错误 E
而失败,或者成功。
为什么不是 Result
?
因为 Result
暗示了输出。以 std::fs::rename
为例
如果我告诉你 rename
函数的返回类型是 Result<T, E>
,你会猜测 T
和 E
分别是什么吗?
你可能正确地假设 E
是 std::io::Error
,但 T 呢?它可能返回任何数量的事物
- 重命名文件目的地的规范路径。
- 移动文件的大小。
- 被重命名文件替换的文件(如果有)的大小。
- 或者甚至可能是被覆盖文件的句柄。
当然,这些都不是真的,因为 rename
的 T
值是单元值 ()
。 rename
永远不会产生任何输出,它只能报告错误。那么为什么不让用户清楚地知道这一点呢?
我认为使用一个表示潜在失败但成功时无输出的类型,可以更清楚地表达使用此函数时的意图和潜在结果。
为什么不使用 Option
呢?
潜在失败可以用 Option<E>
来表达,但如上所述,Option
的 Try
语义使得使用起来不太方便。
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
切换到 Fallible
非常简单,以下是一个示例
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")
}
}
使用 Fallible
fn validate_number(x: u32) -> Fallible<&'static str> {
match x {
0 ..= 9 => Fail("number is too small"),
10..=30 => Success,
31.. => Fail("number is too large")
}
}
兼容性
Fallible
包含将类型映射到和从 Result
和 Option
以及 FromResidual
的实用函数,当与 ?
操作符一起使用时,可以自动执行这些转换。
fn fails_if_true(should_fail: bool) -> Fallible<&'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)
}