#异常 #错误处理 #保证

rewind

强大的异常保证支持类型

1 个不稳定版本

0.1.0 2023 年 5 月 2 日

#2775Rust 模式

GPL-3.0-only

17KB
328

Rewind

它的倒带时间 babeee

License: GPLv3 GitHub Workflow Status

此软件包包含帮助开发具有强异常保证的 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]);

无运行时依赖