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)
}