pub struct Finalize<'a, T: ?Sized, F: FnOnce(&mut T)> {
target: &'a mut T,
finish: Option<F>,
}
impl<'a, T: ?Sized, F: FnOnce(&mut T)> Finalize<'a, T, F> {
pub fn new(target: &'a mut T, finish: F) -> Self {
Self {
target,
finish: Some(finish),
}
}
pub fn run<R>(&mut self, body: impl FnOnce(&mut T) -> R) -> R {
body(self.target)
}
pub fn disarm(mut self) {
self.finish = None;
}
}
impl<T: ?Sized, F: FnOnce(&mut T)> Drop for Finalize<'_, T, F> {
fn drop(&mut self) {
if let Some(finish) = self.finish.take() {
finish(self.target);
}
}
}
#[cfg(test)]
mod tests {
use super::Finalize;
fn early_return(flag: &mut bool) -> Result<(), &'static str> {
let mut guard = Finalize::new(flag, |f: &mut bool| *f = false);
guard.run(|_| Err("boom"))
}
#[test]
fn finalizer_runs_on_an_early_error_exit() {
let mut flag = true;
assert!(early_return(&mut flag).is_err());
assert!(!flag, "the `?` exit must still reach the finalizer");
}
#[test]
fn disarm_suppresses_the_finalizer() {
let mut flag = true;
{
let guard = Finalize::new(&mut flag, |f: &mut bool| *f = false);
guard.disarm();
}
assert!(flag);
}
}