use std;
use std::marker::PhantomData;
pub struct Opaque(());
struct BoxFnVtable<A: ?Sized, F: ?Sized = Opaque> {
call: fn(&F, A),
drop_box: unsafe fn(*mut F),
}
pub struct BoxFn<'a, A: 'a + ?Sized, F: 'a + ?Sized = Opaque> {
data: &'a mut F,
vtable: &'a BoxFnVtable<A, F>,
_invariant: PhantomData<&'a mut &'a ()>,
}
impl<'a, A: ?Sized, F: ?Sized> Drop for BoxFn<'a, A, F> {
fn drop(&mut self) {
unsafe {
(self.vtable.drop_box)(self.data);
}
}
}
impl<'a, A, F: Fn(A)> From<Box<F>> for BoxFn<'a, A, F> {
fn from(f: Box<F>) -> Self {
unsafe fn drop_box<F>(f: *mut F) {
drop(Box::from_raw(f));
}
fn call<F: Fn(A), A>(f: &F, arg: A) {
f(arg)
}
BoxFn {
data: unsafe { &mut *Box::into_raw(f) },
vtable: &BoxFnVtable {
call,
drop_box,
},
_invariant: PhantomData,
}
}
}
impl<'a, A, F> BoxFn<'a, A, F> {
pub fn erase(self) -> BoxFn<'a, A> {
unsafe {
let data = &mut *(self.data as *mut _ as *mut Opaque);
let vtable = &*(self.vtable as *const _ as *const BoxFnVtable<A>);
std::mem::forget(self);
BoxFn {
data,
vtable,
_invariant: PhantomData,
}
}
}
}
impl<'a, A> BoxFn<'a, A> {
pub fn erase_arg(self) -> BoxFn<'a, Opaque> {
unsafe {
let data = &mut *(self.data as *mut _);
let vtable = &*(self.vtable as *const _ as *const BoxFnVtable<Opaque>);
std::mem::forget(self);
BoxFn {
data,
vtable,
_invariant: PhantomData,
}
}
}
}
impl<'a, A, F: ?Sized> BoxFn<'a, A, F> {
#[allow(dead_code)]
pub fn call(&self, arg: A) {
(self.vtable.call)(self.data, arg);
}
}
impl<'a> BoxFn<'a, Opaque> {
pub unsafe fn call_erased<A: 'a>(&self, arg: A) {
std::mem::transmute::<
fn(&Opaque, Opaque),
fn(&Opaque, A),
>(self.vtable.call)(self.data, arg);
}
}