Supporting 'janitorial' style RAII

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.

Example

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);
}

3 Likes