use std::{fmt, os::raw::c_void};
pub(crate) struct ErasedFn {
ptr: *mut c_void,
drop_fn: unsafe fn(*mut c_void),
}
impl ErasedFn {
pub(crate) fn new<F: Send + Sync + 'static>(f: F) -> Self {
unsafe fn drop_glue<F>(p: *mut c_void) { drop(Box::from_raw(p as *mut F)); }
Self {
ptr: Box::into_raw(Box::new(f)) as *mut c_void,
drop_fn: drop_glue::<F>,
}
}
pub(crate) fn as_ptr(&self) -> *mut c_void { self.ptr }
}
unsafe impl Send for ErasedFn {}
unsafe impl Sync for ErasedFn {}
impl Drop for ErasedFn {
fn drop(&mut self) { unsafe { (self.drop_fn)(self.ptr) } }
}
impl fmt::Debug for ErasedFn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ErasedFn").field("ptr", &self.ptr).finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{
atomic::{AtomicU32, Ordering},
Arc,
};
fn roundtrip<F>(f: F, x: u32) -> u32
where
F: Fn(u32) -> u32 + Send + Sync + 'static,
{
let erased = ErasedFn::new(f);
let f: &F = unsafe { &*(erased.as_ptr() as *const F) };
f(x)
}
#[test]
fn boxes_calls_and_drops_without_leak() {
let witness = Arc::new(AtomicU32::new(0));
let captured = witness.clone();
assert_eq!(Arc::strong_count(&witness), 2);
let out = roundtrip(
move |x| {
captured.fetch_add(1, Ordering::Relaxed); x + 1
},
41,
);
assert_eq!(out, 42, "recovered closure must run with correct behaviour");
assert_eq!(
witness.load(Ordering::Relaxed),
1,
"closure body must have executed once"
);
assert_eq!(
Arc::strong_count(&witness),
1,
"ErasedFn::drop must release the closure's captures exactly once"
);
}
}