There's a use case which cannot be solved with current lifetimes.
There was a post about similar issue some time ago (CC Alfriadox).
So the problem. There's a trait, and we need a vtable-entry like function pointer to it.
trait MyObject<'a>: 'a {
// We are using Cell<&'a u8> parameter so this argument could not be made raw pointer
fn add(&self, other: Cell<&'a u8>) -> &'a [u8];
}
fn make_vtable_entry<'a, T: MyObject<'a>>() -> for<'b> unsafe fn(*const (), Cell<&'b u8>) -> &'b [u8] {
???
}
Let's start with this simple version:
fn make_vtable_entry_wrong<'a, T: MyObject<'a>>() -> unsafe fn(*const (), Cell<&'a u8>) -> &'a [u8] {
|x, y| {
let x = &*(x as *const T);
x.add(y)
}
}
it works, but function is tied to lifetime of function parameter. We want to a function for<'b>
.
The correct version would be:
fn make_vtable_entry<'a, T: MyObject<'a>>() -> for<'b> unsafe fn(*const (), Cell<&'b u8>) -> &'b [u8] {
#[cfg(doesnt_work)]
|x, y| {
let x = &*(x as *const T);
// Error: lifetime of reference outlives lifetime of borrowed content
x.add(y)
};
|x, y| {
let x = &*(x as *const T);
// This works, but requires transmuting every argument and return
// to kill lifetimes.
mem::transmute(x.add(mem::transmute(y)))
}
}
What could work:
fn make_vtable_entry<T<'a>: MyObject<'a>>() -> for<'b> unsafe fn(*const (), Cell<&'b u8>) -> &'b [u8] {
|x, y| {
let x = &*(x as *const T<'_>);
x.add(y)
}
}
That would be a new language feature: function type parameters with lifetime parameters.
The same problem could be solved if we pass GAT which exposes T instead of T, like:
trait MyObjectConstructor {
type MyObject<'a>: MyObject<'a>,
}
but having to write higher kinder trait for each implementation of MyObject
and having to pass it explicitly in many functions is somewhat unergonomic.
- Edit: added
unsafe
to functions - Edit: changed function parameter to
*const ()