For this specific case, since you're doing manual type erasure which is wildly unsafe already, I think it's fine if you have to transmute
away some lifetimes to make it work. You only need a single transmute
to make the pointer type more general:
pub trait MyObject<'a>: 'a {
fn add(&self, other: Cell<&'a u8>) -> &'a [u8];
}
pub fn make_vtable_entry_concrete<'a, T: MyObject<'a>>(
) -> for<'b> fn(&'b T, Cell<&'a u8>) -> &'a [u8] {
|x, y| x.add(y)
}
pub fn make_vtable_entry_erased<'a, T: MyObject<'a>>(
) -> for<'b> unsafe fn(*const (), Cell<&'b u8>) -> &'b [u8] {
unsafe { mem::transmute(make_vtable_entry_concrete::<T>()) }
}
IIUC this makes no more assumptions than already introduced by using manual vtables.
Given that "lifetimes don't exist," though, I do think lifetime-specific HKT could be reasonable, the same way that we already allow for<'a>
in other positions. That could look like
pub fn make_vtable_entry<for<'a> T<'a>: MyObject<'a>>(
) -> for<'a, 'b> fn(&'b T<'a>, Cell<&'a u8>) -> &'a [u8] {
|x, y| x.add(y)
}
This would have to be called with an explicit turbofish, e.g.
make_vtable_entry::<for<'a> &'a u8>()
Your HKT signature cannot work for Foo
as written, though, @stepancheg, as in the body of make_vtable_entry
you can name T<'static>
which may not be valid Foo<'v, V>
for non-'static
V
. There needs to be an extra 'bound
lifetime, which is where all of the additional complexity is coming from, e.g.
pub fn make_vtable_entry<'bound, for<'a where 'bound: 'a> T<'a>: MyObject<'a>(
) -> for<'a, 'b where 'bound: 'a> fn(&'b T<'a>, Cell<&'a u8>) -> &'a [u8] {
|x, y| x.add(y)
}
make_vtable_entry::<'v, for<'a where 'v: 'a> Foo<'a, V>>()
or perhaps use '_
for single-lifetime sugar. The for<where>
syntax is from Extending `for<'a>` construct.
I tried to explicitly reify this with GAT, but ran into a cryptic lifetime bound not satisfied
error that doesn't even provide the source of the bound.
pub trait TMyObject<'bound> {
type Apply<'a>: MyObject<'a>
where
'bound: 'a;
}
pub fn make_vtable_entry<'bound, Kind: TMyObject<'bound>>(
) -> for<'a, 'b> fn(&'b Kind::Apply<'a>, Cell<&'a u8>) -> &'a [u8] {
|x, y| x.add(y)
}
pub struct Foo<'v, V: 'v> {
// invariance for maximum pessimism; I tried covariance as well
_phantom: PhantomData<fn(&'v V) -> &'v V>,
}
impl<'v, V: 'v> TMyObject<'v> for Foo<'v, V> {
type Apply<'a> = Foo<'a, V>
where
'v: 'a;
}
impl<'v, V: 'v> MyObject<'v> for Foo<'v, V> {
fn add(&self, _other: Cell<&'v u8>) -> &'v [u8] { unimplemented!() }
}
pub fn make_foo_vtable_entry<'v, V: 'v>(
) -> for<'a> fn(&'a Foo<'v, V>, Cell<&'v u8>) -> &'v [u8] {
make_vtable_entry::<'v, Foo<'v, V>>()
}