try_finally

Function try_finally 

Source
pub async fn try_finally<B, F, BFut, FFut, Out, Err>(
    body: B,
    finally: F,
) -> Result<Out, Err>
where B: FnOnce() -> BFut + UnwindSafe, BFut: Future<Output = Result<Out, Err>>, F: FnOnce() -> FFut, FFut: Future<Output = Result<(), Err>>,
Expand description

An approximation of a “try/finally” block for async Result-returning code.

This function can be used to simulate a “try/finally” block that may be familiar from languages with an exception system. The provided body closure will be called and awaited and its result returned. The provided finally closure will be called and awaited at the completion of the body, regardless of whether the body returned successfully, returned an error, or panicked.

This pattern can be useful for running cleanup code, for example in cleaning up after running a test. Rust code would typically use a scope guard with a Drop impl for doing such cleanup work, but that doesn’t handle code that needs to be async.

§Example

use cobalt_async::try_finally;
use anyhow::Ok;

let mut cleaned_up = false;

let res = try_finally(
  || async {
    Ok("the body ran successfully")
  },
  || async {
    cleaned_up = true;
    Ok(())
  }
).await.unwrap();

assert_eq!(res, "the body ran successfully");
assert!(cleaned_up);

If the body closure references state that is not UnwindSafe, you may need to manually annotate it with AssertUnwindSafe, and ensure that the finally closure does not manipulate that state in a way that would violate unwind safety.

use cobalt_async::try_finally;
use anyhow::Ok;
use std::cell::RefCell;

let mut cleaned_up = false;
let mut data = RefCell::new(0);

// If the body code panics, we promise not to look at `data` in the finally code.
let data = std::panic::AssertUnwindSafe(data);

let res = try_finally(
  || async {
    Ok(data.replace(42))
  },
  || async {
    cleaned_up = true;
    Ok(())
  }
).await.unwrap();

assert_eq!(res, 0);
assert_eq!(data.take(), 42);
assert!(cleaned_up);

§Notes

  • As with Drop, running the finally closure on panic is not guaranteed. In particular it will not be run when the panic mode is set to abort.

  • Panics in the finally code will not be caught, and may potentially mask panics from the body code.

  • Errors returned by the finally code will be returned to the caller, potentially masking errors returned by the body code.