Sometimes I need to use type erasure to reduce binary size:
fn foo(f: &mut dyn FnMut(T) -> U) {
...
// Code that calls `f`.
...
}
I am wondering if this can be further optimized. As far as I know, dyn Trait
needs a vtable to work, and the vtable contains metadata like size, align, drop_in_place
address, etc. But in this specific case, most of the metadata is not used, so I imagine a wrapper type that behaves like a &mut dyn FnMut(Foo) -> Bar
, but without any unnecessary vtable stuff, something like this:
pub struct RefFnMut1<'a, T, U> {
// The address the `FnMut` object,
data: NonNull<()>,
// The address of a wrapper function that calls `FnMut::call_mut` function.
call_mut_fn: unsafe fn(NonNull<()>, T) -> U,
_phantom: PhantomData<&'a mut ()>,
}
impl<'a, T, U> RefFnMut1<'a, T, U> {
pub fn new<F>(f: &'a mut F) -> Self
where
F: FnMut(T) -> U,
{
...
}
pub fn call_mut(&mut self, arg: T) -> U {
unsafe { (self.call_mut_fn)(self.data, arg) }
}
}
Here is a proof of concept playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ba86efedf940958705c3acc06c908fff.
Basically, this is like a inlined vtable, but only contains useful information.
Is there any problem with this pattern? Is there any existing crate that implements this pattern? I am not very confident with writing unsafe codes, so an existing implementation will be extremely helpful.