You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
2.9 KiB

#[allow(unused_imports)]
use std::{
ops::{
Drop,
Deref,
DerefMut,
},
mem::{
replace,
MaybeUninit,
},
fmt,
};
use cfg_if::cfg_if;
#[cfg(not(debug_assertions))]
type OptFn<T,F> = MaybeUninit<(T,F)>;
#[cfg(debug_assertions)]
type OptFn<T,F> = Option<(T,F)>;
/// Allow for running function on drop, to support moving out of a larger structure.
///
/// # Notes
/// This type has a safe ABI with runtime checks when compiled with `debug_assertions`, without them an unsafe but lower cost ABI is preferred.
pub struct PhantomDrop<T,F>(OptFn<T,F>)
where F: FnOnce(T);
impl<T,F> fmt::Debug for PhantomDrop<T,F>
where F:FnOnce(T),
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
self.deref().fmt(f)
}
}
impl<T,F> Drop for PhantomDrop<T,F>
where F:FnOnce(T)
{
fn drop(&mut self) {
cfg_if! {
if #[cfg(debug_assertions)] {
if let Some((value, func)) = replace(&mut self.0, None)
{
func(value);
} else {
panic!("Double drop?")
}
} else {
let (value, func) = unsafe {
(&mut self.0 as *mut OptFn<T,F>).read().assume_init()
};
func(value);
}
}
}
}
impl<T,F> PhantomDrop<T,F>
where F:FnOnce(T)
{
/// Create a new `PhantomDrop` with a drop closure and a value.
#[cfg(nightly)]
pub const fn new(value: T, func: F) -> Self
{
cfg_if! {
if #[cfg(debug_assertions)] {
Self(Some((value,func)))
} else {
Self(MaybeUninit::new((value,func)))
}
}
}
/// Create a new `PhantomDrop` with a drop closure and a value.
#[cfg(not(nightly))]
pub fn new(value: T, func: F) -> Self
{
cfg_if! {
if #[cfg(debug_assertions)] {
Self(Some((value,func)))
} else {
Self(MaybeUninit::new((value,func)))
}
}
}
#[cfg(not(debug_assertions))]
#[inline]
fn get_ref<'a>(&'a self) -> &'a (T, F)
{
unsafe {
cfg_if! {
if #[cfg(nightly)] {
self.0.get_ref()
} else {
std::mem::transmute(self.0.as_ptr())
}
}
}
}
#[cfg(not(debug_assertions))]
#[inline]
fn get_mut<'a>(&'a mut self) -> &'a mut (T, F)
{
unsafe {
cfg_if! {
if #[cfg(nightly)] {
self.0.get_mut()
} else {
std::mem::transmute(self.0.as_mut_ptr())
}
}
}
}
}
impl<T,F> Deref for PhantomDrop<T,F>
where F:FnOnce(T)
{
type Target = T;
fn deref(&self) -> &Self::Target
{
cfg_if! {
if #[cfg(debug_assertions)] {
if let Some((t, _)) = &self.0
{
t
} else {
panic!("Double drop?")
}
} else {
let (t, _) = self.get_ref();
t
}
}
}
}
impl<T,F> DerefMut for PhantomDrop<T,F>
where F:FnOnce(T)
{
fn deref_mut(&mut self) -> &mut <Self as Deref>::Target
{
cfg_if! {
if #[cfg(debug_assertions)] {
if let Some((t, _)) = &mut self.0
{
t
} else {
panic!("Double drop?")
}
} else {
let (t, _) = self.get_mut();
t
}
}
}
}