Option 1:
// somewhere
fn central_cleanup_definition(this: &mut This) {
// cleanup
}
// anywhere
let this = Janitor::new(this, central_cleanup_definition);
Option 2:
Create a simple macro to define a custom janitor type that encapsulates the Deref
,DerefMut
,Drop
boilerplate.
macro_rules! janitor {
{
$vis:vis $janitor:ident($this:ident: &mut $This:ty $(,$pat:ident: $ty:ty)* $(,)?) { $($tt:tt)* }
} => {
#[derive(Debug)]
$vis struct $janitor<'a> {
$this: &'a mut $This,
$($pat: std::mem::ManuallyDrop<$ty>,)*
}
#[allow(non_snake_case)]
$vis fn $janitor($this: &mut $This $(,$pat: $ty)*) -> $janitor<'_> {
$janitor {
$this,
$($pat: std::mem::ManuallyDrop::new($pat),)*
}
}
impl std::ops::Deref for $janitor<'_> {
type Target = $This;
fn deref(&self) -> &$This { &self.$this }
}
impl std::ops::DerefMut for $janitor<'_> {
fn deref_mut(&mut self) -> &mut $This { &mut self.$this }
}
impl Drop for $janitor<'_> {
fn drop(&mut self) {
let $janitor { $this $(,$pat)* } = self;
let $this = &mut **$this; // &mut &mut $This -> &mut $This
$(let $pat = unsafe { std::mem::ManuallyDrop::take($pat) };)*
{ $($tt)* }
}
}
};
{
$vis:vis $janitor:ident($this:ident: &$This:ty $(,$pat:ident: $ty:ty)* $(,)?) { $($tt:tt)* }
} => {
#[derive(Debug)]
$vis struct $janitor<'a> {
$this: &'a $This,
$($pat: std::mem::ManuallyDrop<$ty>,)*
}
#[allow(non_snake_case)]
$vis fn $janitor($this: &$This $(,$pat: $ty)*) -> $janitor<'_> {
$janitor {
$this,
$($pat: std::mem::ManuallyDrop::new($pat),)*
}
}
impl std::ops::Deref for $janitor<'_> {
type Target = $This;
fn deref(&self) -> &$This { &self.$this }
}
impl Drop for $janitor<'_> {
fn drop(&mut self) {
let $janitor { $this $(,$pat)* } = self;
let $this = & **$this; // &mut &$This -> &$This
$(let $pat = unsafe { std::mem::ManuallyDrop::take($pat) };)*
{ $($tt)* }
}
}
};
}
janitor! {
pub ResetJanitor(flag: &mut bool, val: bool) {
*flag = val;
}
}
janitor! {
pub TestJanitor(_flag: &bool, _val: bool) {}
}
fn main() {
let mut b = true;
{
let b = ResetJanitor(&mut b, false);
dbg!(b);
}
dbg!(b);
}