#![deny(missing_debug_implementations, nonstandard_style)]
#![warn(missing_docs, future_incompatible, unreachable_pub)]
use core::mem::ManuallyDrop;
use core::ops::{Deref, DerefMut};
use core::ptr;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Guard<T, F>
where
F: FnOnce(T),
{
inner: ManuallyDrop<T>,
f: ManuallyDrop<F>,
}
impl<T, F> Guard<T, F>
where
F: FnOnce(T),
{
pub fn new(inner: T, f: F) -> Self {
Self {
inner: ManuallyDrop::new(inner),
f: ManuallyDrop::new(f),
}
}
#[inline]
pub fn into_inner(guard: Self) -> T {
let mut guard = ManuallyDrop::new(guard);
unsafe {
let value = ptr::read(&*guard.inner);
ManuallyDrop::drop(&mut guard.f);
value
}
}
}
impl<T, F> Deref for Guard<T, F>
where
F: FnOnce(T),
{
type Target = T;
fn deref(&self) -> &T {
&*self.inner
}
}
impl<T, F> DerefMut for Guard<T, F>
where
F: FnOnce(T),
{
fn deref_mut(&mut self) -> &mut T {
&mut *self.inner
}
}
impl<T, F> Drop for Guard<T, F>
where
F: FnOnce(T),
{
fn drop(&mut self) {
let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
let f = unsafe { ManuallyDrop::take(&mut self.f) };
f(inner);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
#[test]
fn test_only_dropped_by_closure_when_run() {
let value_drops = Cell::new(0);
let value = Guard::new((), |()| value_drops.set(1 + value_drops.get()));
let closure_drops = Cell::new(0);
let guard = Guard::new(value, |_| closure_drops.set(1 + closure_drops.get()));
assert_eq!(value_drops.get(), 0);
assert_eq!(closure_drops.get(), 0);
drop(guard);
assert_eq!(value_drops.get(), 1);
assert_eq!(closure_drops.get(), 1);
}
#[test]
fn test_into_inner() {
let dropped = Cell::new(false);
let value = Guard::new(42, |_| dropped.set(true));
let guard = Guard::new(value, |_| dropped.set(true));
let inner = Guard::into_inner(guard);
assert_eq!(dropped.get(), false);
assert_eq!(*inner, 42);
}
}