When is transmuting lifetimes useful?

I was looking up intrinsics in the standard library documentation and came across this example for transmute:

struct R<'a>(&'a i32);
unsafe fn extend_lifetime<'b>(r: R<'b>) -> R<'static> {
    std::mem::transmute::<R<'b>, R<'static>>(r)
}

unsafe fn shorten_invariant_lifetime<'b, 'c>(r: &'b mut R<'static>)
                                             -> &'b mut R<'c> {
    std::mem::transmute::<&'b mut R<'static>, &'b mut R<'c>>(r)
}

I'm not an expert on how Rust lifetimes work internally and how they behave, but I know that if a normal Rust user would write such code they'd be called out for it immediately (also, the documentation explicitly states this is "very advanced Rust").

Therefore I was wondering whether you know any example of when such "advanced" code is used in practice?

I mean, there's a reason that the docs for transmute say:

transmute is incredibly unsafe. There are a vast number of ways to cause undefined behavior with this function. transmute should be the absolute last resort.

The nomicon is even more explicit:

This is really, truly, the most horribly unsafe thing you can do in Rust. The guardrails here are dental floss.

1 Like

I've only seen it in one place so far -- to work around lifetime issues of self-referential structs e.g.: backtrace-rs/gimli.rs at master · rust-lang/backtrace-rs · GitHub

That said, I'm rather new to Rust so I'm sure this is not the only real world use case.

Edit: Here's a shorter example of the above: Saving variables interwoven by reference in one structure - #13 by alice

1 Like

Mutable iterators are a common case where this is necessary. There's no way to explain to the borrow checker that next() returns a different, non-overlapping mutable borrow each time if they all come from the same source. This can be reduced to implementation of slice.split_at_mut(), which also requires such fudge.

7 Likes

Another example are scoped threads. std::thread::spawn requires the closure to be 'static because it may outlive the calling functions. There are however crates like crossbeam that provide scoped threads by automatically joining them before returning. However they still need the closure to be 'static to pass it to std::thread::spawn, and the mean it uses to achieve that is transmute. See this portion of code for example.

5 Likes

Another use case is intentionally leaking memory but still refering to it (with a &'static).

1 Like

transmute won't help you with that. Yes, it will allow creating a 'static reference, but that reference won't really live for the 'static lifetime, so it would be unsound to consider it really 'static.

If you want to leak values and get a 'static reference back you should just use the safe function Box::leak

2 Likes

Right, I was thinking of Box::leak() but should have specified that. And yeah, it's generating a lifetime arbitrarily, not transumting one. Thank you for the correction.

1 Like