1 个不稳定版本
0.1.0 | 2022 年 5 月 22 日 |
---|
#2684 在 Rust 模式
11KB
85 行
灾难传递
这是一个具有两个辅助特性和一个辅助函数的库,用于简洁性,以及用于 DRY 代码的辅助函数。
通过灾难传递的值通过抛出异常返回,并展开栈。如果遇到 panic 时中断程序,则此库将无法正常工作。
这是对参数的工作方式?通过 FnOnce
,或者说,相当于 FnOnce
,通常是一个 FnOnce
但不一定必须是 FnOnce
:一个 DisasterWaitingToHappen
是我创建的类型,因为 Rust 要求在除函数返回类型之外的其他地方使用 !
类型时,必须启用夜间功能。因此,DisasterWaitingToHappen
事实上是一个 FnOnce() -> !
,只是在那个目前还不稳定的位子没有使用 !
。由于 !
可以转换为任何类型,这样的函数也将是一个 FnOnce() -> Never
(Never
是一个私有实现细节),并且它们实现了 DisasterWaitingToHappen
。基本上,参数是通过传递一个函数,而不是 x
,而是传递 || std::panic::panic_any(x)
来传递的。
默认情况下,此包确实需要夜间工具链以使用 never
夜间功能,但功能标志 stable
将使此库使用一个无法实例化的枚举类型。我认为这实际上没有任何区别,因为这不是公共签名的一部分,而且 !
也可以转换为那种私有类型。
由于展开的栈被类型化为 dyn Any
,因此在读取灾难函数的返回值时,必须重新指定其类型。类型推导显然对为什么你要这样做一无所知,并拒绝履行其职责。
- 当前Rust不允许将
impl
和普通泛型参数同时用于同一个函数 Catastrophe
是无类型的,因此为了让泛型函数正常工作,你需要显式指定它们
因此,你无法在泛型函数中使用impl
DisasterWaitingToHappen。你必须传递显式的F: DisasterWaitingToHappen
Disaster
WaitingToHappen
通常是未命名的闭包,这意味着你不能将它们放在泛型参数的位置- Rust不允许省略参数以推断它们,因此你必须指定灾难类型
- Rust确实允许你指定一个泛型类型为_,表示“请推断”,而且这行得通。
因此,泛型灾难函数几乎总是需要与参数数量相等的_
泛型参数。
唯一例外是在将值赋给非灾难值时,例如let
绑定,或者实际有用的东西。在这种情况下,你可以完全省略泛型,让Rust自己推断。
catastrophe!
宏有两个用途
- 如果你用某些表达式调用它,这些表达式是灾难函数调用的结果(即
!
),它将评估该表达式并将其强制转换为给定的类型。 - 如果你给它几个
Disaster
WaitingToHappen
(我讨厌当类型名变得如此之长,以至于复数形式实际上是在中间修改某个词时),你必须手动执行它们,并提供它们各自的返回类型。结果是元组。
这两个都是同一个宏的实现,所以如果你想的话,你可以用它来将多个函数调用组合成元组。实际上,DisasterWaitingToHappen
只是一个具有灾难函数的特质,该函数恰好被命名为HALT_AND_CATCH_FIRE
。因为它们是相同的实现,具有相同的语法,所以你必须调用该函数。一个合适的名字,因为它确实会停止程序执行并开始堆栈回溯。
这个库中的内容可以帮助将以下代码
use std::ops::Add;
use std::panic::UnwindSafe;
use std::panic::panic_any;
use never::Never;
fn add4<Num: Add<Num, Output = Num>, A, B, C, D>(a: A, b: B, c: C, d: D) -> !
where
Num: 'static + Send,
A: FnOnce() -> Never + UnwindSafe,
B: FnOnce() -> Never + UnwindSafe,
C: FnOnce() -> Never + UnwindSafe,
D: FnOnce() -> Never + UnwindSafe,
{
let a = *std::panic::catch_unwind(a).unwrap_err().downcast::<Num>().unwrap();
let b = *std::panic::catch_unwind(b).unwrap_err().downcast::<Num>().unwrap();
let c = *std::panic::catch_unwind(c).unwrap_err().downcast::<Num>().unwrap();
let d = *std::panic::catch_unwind(d).unwrap_err().downcast::<Num>().unwrap();
panic_any(a + b + c + d);
}
简化为这样
use std::ops::Add;
use std::panic::panic_any;
use pass_by_catastrophe::Catastrophic;
use pass_by_catastrophe::DisasterWaitingToHappen;
fn add4<Num: Catastrophic, A, B, C, D>(a: A, b: B, c: C, d: D) -> !
where
Num: Add<Num, Output = Num>,
A: DisasterWaitingToHappen,
B: DisasterWaitingToHappen,
C: DisasterWaitingToHappen,
D: DisasterWaitingToHappen,
{
let (a, b, c, d) = catastrophe!(
a.HALT_AND_CATCH_FIRE() => Num,
b.HALT_AND_CATCH_FIRE() => Num,
c.HALT_AND_CATCH_FIRE() => Num,
d.HALT_AND_CATCH_FIRE() => Num,
);
panic_any(a + b + c + d);
}
不过,这并不是一个公平的比较,因为下面使用catastrophe!
宏的代码将正确处理类型不正确的恐慌,并且它还将正确处理a
、b
、c
、d
中的任何一个实际上没有恐慌而是返回的情况。顺便说一下,这是一个在tests.rs
中的测试
我推荐magic-import
这个crate,它允许进一步简化示例的顶部
magic_import::magic!();
fn add4(...) { ... }
哦,还有,你知道最好的部分是什么吗?如果你在程序中传递灾难,然后在某处尝试捕获一个i32
,但代码实际上返回了一个i64
,那么错误将一直传播到找到寻找i64
的灾难着陆区。享受调试吧,混蛋!
依赖关系
~465KB