#builder-pattern #exception #unwind #safety #unsafe #finally

unwind_safe

Readable unwind-safe code thanks to a try-finally-looking builder pattern

3 unstable releases

0.1.0 Dec 31, 2021
0.0.1 Mar 5, 2021

#2198 in Rust patterns

Download history 12746/week @ 2024-07-24 11989/week @ 2024-07-31 11475/week @ 2024-08-07 13943/week @ 2024-08-14 15255/week @ 2024-08-21 17930/week @ 2024-08-28 17560/week @ 2024-09-04 16804/week @ 2024-09-11 12720/week @ 2024-09-18 16154/week @ 2024-09-25 20667/week @ 2024-10-02 13578/week @ 2024-10-09 20349/week @ 2024-10-16 19628/week @ 2024-10-23 17601/week @ 2024-10-30 15599/week @ 2024-11-06

76,045 downloads per month
Used in 34 crates (3 directly)

Zlib OR MIT OR Apache-2.0

10KB
118 lines

::unwind_safe

Repository Latest version Documentation MSRV License CI

Readable unwind-safe code thanks to a try-finally-looking builder pattern

let mut body_called = false;
let mut finally_called = false;

// Let's imagine some code being run in a context where
// panics do not affect us (`panic::catch_unwind`), or some
// executor running stuff on another thread…
let _ = ::crossbeam::thread::scope(|s| drop(s.spawn(|_| {

    let ft = {
        ::unwind_safe::with_state(())
            .try_eval(|_| {
                body_called = true;
                if ::rand::random() {
                    panic!();
                } else {
                    42
                }
            })
            .finally(|_| { // <- The point of this crate!
                finally_called = true;
            })
    };
    // This is only reached when `try_eval` does not panic, obviously.
    assert_eq!(ft, 42);

})));

// Whatever code path was taken, the finally block is always executed
// (that's the point of this crate!).
// From a place that survives the panic (if any), we thus observe:
assert!(body_called);
assert!(finally_called);

With an actual owned state

If the destructor requires access to an owned State1 in the finally / deferred block, (type State =: of your choosing),

  1. you can feed that to the ::unwind_safe::with_state::<State> API entry-point;

  2. the .try_eval(|state: &mut State| {}) block will then have access to an exclusive borrow (&mut) to it through the closure's parameter,

  3. and the .finally block will get access to that state in an owned fashion through its own closure parameter: .finally(|state: State| {}).

1 This "owned" state may still be a borrow, e.g., type State = &mut;

Can unsafe code rely on the finally code always being run?

Yes! That's the point of the crate, and why it is so named: you can use this .finally pattern to ensure your unsafe code is unwind-safe ✅

Similar to ::scopeguard

This is similar to ::scopeguard::defer!, but for the added ability to get owned access in the finally / defer-red block while still letting the main block have &mut references to it.

It is thus actually the same as ::scopeguard::guard! The only (but crucial, imho) difference between these two is the readability of the code: with .try_eval().finally(), it is more obvious that the code in the .finally() part is running after the one on the main block, which is not obvious at first sight with ::scopeguard's API (it requires knowing how it works).

No runtime deps