1 个不稳定版本
0.1.0 | 2023 年 5 月 2 日 |
---|
#2775 在 Rust 模式
17KB
328 行
Rewind
它的倒带时间 babeee
此软件包包含帮助开发具有强异常保证的 API 的实用工具。基本上,如果函数以某种方式失败,那么它应该像函数从未被调用过一样。
在 C# 和 Java 等语言中,这可以通过 finally
块来完成。然而,Rust 目前没有方法“捕获”除将其放入 lambda 之外以外的 ?
返回,而且有时你希望有更细粒度的控制,而不是整个块。例如,当你有多个依赖于上一个操作的多个操作时,块语句相当难以处理
#[derive(Default)]
struct Stack<T> {
els: Vec<T>,
}
impl <T> Stack<T> {
pub fn pop(&mut self) -> Result<T, ()> {
self.els.pop().ok_or(())
}
pub fn get(&self, index: usize) -> Result<&T, ()> {
self.els.get(index).ok_or(())
}
pub fn push(&mut self, el: T) {
self.els.push(el);
}
}
fn may_fail() -> Result<(), ()> { Err(()) }
let mut s = Stack::<i32>::default();
let result = (|| {
s.push(4);
s.push(5);
let value = s.pop()?;
may_fail()?;
println!("{}", value);
Ok::<(), ()>(())
})();
if result.is_err() {
s.push(4);
}
assert_eq!(s.els, vec![4, 4]); // uh oh
如注释所示,上面的代码将把 4
压入堆栈两次。虽然这个例子很简单,但这种错误很容易在副作用复杂的代码中发生。现在让我们看看使用 rewind
的这段代码
use rewind::Atom;
#[derive(Default)]
struct Stack<T> {
els: Vec<T>,
}
impl<T> Stack<T> {
pub fn pop(&mut self) -> Result<T, ()> {
self.els.pop().ok_or(())
}
pub fn push(&mut self, el: T) {
self.els.push(el);
}
}
fn may_fail() -> Result<(), ()> {
Err(())
}
let mut s = rewind::encase(Stack::<i32>::default());
let result = (|| {
s.push(4);
s.push(5);
let value = s.peel_mut(
|s| s.pop(),
|s, v| {
if let Ok(v) = v {
s.push(v);
}
},
);
may_fail()?;
println!("{}", value.decay()?);
Ok::<(), ()>(())
})();
assert!(result.is_err());
assert_eq!(s.els, vec![4, 5]);