Unsafe lifetime erasure from `for<'a> T + 'a` to `T: 'unsafe`

Edit: My problem is resolved. TAIT RFC already discussed this and it is partially implemented in the compiler, and those unimplemented parts confused me.

I propose to add a way to unsafely erase lifetimes from any type, generically.

One long-lived example from the ecosystem is Google's yoke - Rust Yokable trait for those purposes. They are replacing lifetimes with 'static, but actual lifetime is tied to something like Rc. It is their approach to generically transform Foo<'a> to Foo<'static>. But their approach only works for types you can slap derive macro on and it's enough for their ser/de purposes. It does not work for futures, my primary use case.

I have a need to store allocations in static variables instead of the heap due to predictability. If you want to use heap, then Box::leak is 100% sufficient. And for T: 'static crates like static_cell - Rust are working fine. The problem with those for me: if you want to store non-'static data, then you'll be bitten by invariance - you can't have &'static mut Foo<'a>, but borrow checker enforces that relation to exists and fails.

My problem is, you cannot in general case store Foo<'a> inside static. For "Foo<'a>" you can write static mut FOO: Foo<'static> and then store Foo<'a> here. But often Foo is unnamable - for example, large future. Even TAIT would not help, because it would allow you to name Foo<'a>, but for that unsafe trick to work you need to rewrite Foo<'a> to Foo<'static>, and you just cant...

I wrote that proof of concept (playground) that uses generic_const_expressions to (1) construct type equivalent to T by alignment and size and (2) verify that alignment and size of T are matching to declared ones. It passes miri for a couple of tests.

If you want to provide advice or solution, please do that after confirming that it would work with my example. Thank you.

How can one approach to get this use case get supported in Rust? Specifically, ergonomics of Box but with memory in static. I don't think that my solution with generic_const_expressions is viable.

1 Like

if you want to construct a type that has the same size and alignment as T, but that is valid for any bit pattern, can't you just use MaybeUninit<T>? then use a SyncUnsafeCell or static mut?

you might also be interested in the idea of contravariance

also, that playground link you posted seems broken, possibly it is too long.

You can't do it in Rust generically, even with MaybeUninit. Even with MaybeUninit and SafeSyncCell there still would be lifetimes and you can't place if in a static variable

is it even UB to construct a reference with a lifetime longer than the lifetime of its allocation, as long as the reference doesn't actually live that long during program execution?

now, exposing that reference to user code would be unsound, but i don't think it would be immediately UB the same way constructing a null reference is.

  1. No, it is not ub
  2. I didn't understand you

if you expose it from your public api, someone could hold onto that reference past when the backing allocation is freed, and that would be UB.

Thank you for clarification. This has no relation to the issue. Please check playground link I provided, that showcases the usage

the playground link you provided is broken.

also, it's extremely relevant, you can take your data and transmute it to a static lifetime (this would subvert a lot of the safety features of rust, but hey, C programmers have been manually managing lifetimes for a while)

I've updated the link

here's how you can use MaybeUninit instead of generic_const_exprs.

playground

Hello, thank you for your effort. This version of code bacame unsound, because you can, for example, allocate 0u128 and get segfault. Additionaly, here Foo got implicit 'static generic, but this is because Foo is generic over lifetime. There exist types that are not, namely those produced by TAIT - they have lifetime, but they are not generic - so this approach would fail. My use case is exactly TAIT futures.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.