# Lifetime erasure type

**URL:** <https://internals.rust-lang.org/t/lifetime-erasure-type/13001>\
**Category:** language design\
**Created:** [September 4, 2020, 11:31am UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001 "2020-09-04T11:31:18Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![waffle](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/waffle/32/14371_2.png) [@waffle](https://internals.rust-lang.org/u/waffle)\
**Post date:** [September 4, 2020, 11:31am UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/1 "2020-09-04T11:31:18Z")

</div>

tl;dr: I want a type `EraseLife<T>` such that `for<T> EraseLife<T>: 'static` (i.e.: `EraseLife` is always `'static` even if `T = &'a Ty`)

My use case is to use `TypeId` to specialize for known types to remove some safety checks. E.g.

```rust
if !trusted::<T>() {
    // do some safety check
}

fn trusted<T>() -> bool {
    // w/o ErasedLife this wouldn't work because T is not 'static
    TypeId::of::<ErasedLife<T>>() == TypeId::of::<ErasedLife<String>>()
        // because of ErasedLife this would return true for str with any lifetime
        || TypeId::of::<ErasedLife<T>>() == TypeId::of::<ErasedLife<&'static str>>() 
        || TypeId::of::<ErasedLife<T>>() == TypeId::of::<ErasedLife<Cow<'static, str>>>() 
}

```

This does **not** need to transmute types and as such should be perfectly safe, right?

I couldn't find a way to create such a type without compiler support. Is it even possible to implement something like that in the compiler? Does it break something that I've missed?

I don't think that this would ever be implemented, but want to dream for a bit 😄

---

<div class="post-metadata">

**Author:** ![Ixrec](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ixrec/32/6754_2.png) [@Ixrec](https://internals.rust-lang.org/u/Ixrec)\
**Post date:** [September 4, 2020, 3:18pm UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/2 "2020-09-04T15:18:52Z")

</div>

The absence of an explicit transmute() call is not an argument that this is sound. There's plenty of other ways to cause UB with unsafe code.

AFAIK what you're describing is just fundamentally unsound. `Box<T>` can already do some things you might call "lifetime erasure" that are clearly sound, but boxing a dangling reference obviously won't make it any less dangling.

---

<div class="post-metadata">

**Author:** ![waffle](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/waffle/32/14371_2.png) [@waffle](https://internals.rust-lang.org/u/waffle)\
**Post date:** [September 4, 2020, 3:48pm UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/3 "2020-09-04T15:48:30Z")

</div>

> [@Ixrec](#):
>
> AFAIK what you're describing is just fundamentally unsound.

Why? Can you show me how could I cause `UB` with `ErasedLife` in safe code?

I agree, if it would allow accessing and/or setting values of type `T` then it would be unsound. But it's just an analog to `PhamtomData` what could go wrong?

---

<div class="post-metadata">

**Author:** ![Ixrec](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ixrec/32/6754_2.png) [@Ixrec](https://internals.rust-lang.org/u/Ixrec)\
**Post date:** [September 4, 2020, 3:53pm UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/4 "2020-09-04T15:53:22Z")

</div>

> [@waffle](#):
>
> Why? Can you show me how could I cause `UB` with `ErasedLife` in safe code?
> 
> I agree, if it would allow accessing and/or setting values of type `T` then it would be unsound. But it's just an analog to `PhamtomData` what could go wrong?

I don't think you've suggested an actual API for this, so I doubt we can give any answer more precise than what I already said. For example, I could obviously show you how using transmute() to completely bypass the borrow checker can trivially cause UB, but I doubt that's what you're asking for (and the Rustonomicon already covers that). And if this actually is equivalent to PhantomData, then it's even more unclear what you think you're proposing; why not actually use PhantomData?

The onus is on the change proposer to actually propose a concrete, specific change, especially if you want concrete, specific feedback.

---

<div class="post-metadata">

**Author:** ![felix.s](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/felix.s/32/8073_2.png) [@felix.s](https://internals.rust-lang.org/u/felix.s)\
**Post date:** [September 4, 2020, 4:17pm UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/5 "2020-09-04T16:17:34Z")

</div>

I think it's pretty evident from the OP: a type constructor `EraseLife` such that `EraseLife<T>` has lifetime `'static` but is otherwise equivalent to `()` (or even `!`), for the purpose of type-level operations. (Apparently `PhantomData` lacks the former property.)

I'm not necessarily in favour of this, but it's hardly unclear what is being proposed.

---

<div class="post-metadata">

**Author:** ![waffle](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/waffle/32/14371_2.png) [@waffle](https://internals.rust-lang.org/u/waffle)\
**Post date:** [September 7, 2020, 12:17pm UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/6 "2020-09-07T12:17:54Z")

</div>

I thought that what I'm proposing is actually clear... I'll try to explain better. I propose API (if you may call it so) like this:

````rust
// mod core::marker

/// A marker empty type that is `'static` even if `T` is not.
/// 
/// ## Examples
/// 
/// ```
/// fn assert_static<T: 'static>() {}
/// 
/// fn lifetimed<'a>(_: &'a ()) {
/// assert_static::<EraseLife<&'a ()>>();
/// }
/// ```
/// 
/// Integration with [`TypeId`](core::any::TypeId):
/// ```
/// use core::any::type_id;
/// 
/// fn test<'a, 'b>(_a: &'a str, _b: &'b str) {
/// // `ErasedLife` ignores lifetimes so this won't fail even if 'a != 'b
/// // This DOES NOT mean that `_a` and `_b` has the same type
/// // it only means that they have the same types _if you'll ignore lifetimes_ 
/// assert_eq!(type_id::<ErasedLife<&'a str>>(), type_id::<ErasedLife<&'b str>>())
/// }
/// 
/// test(String::new().as_str(), "");
/// ```
#[lang_item = "erase_lifetimes"]
pub struct EraseLife<T>;

````

---

<div class="post-metadata">

**Author:** ![Michael-F-Bryan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/michael-f-bryan/32/2815_2.png) [@Michael-F-Bryan](https://internals.rust-lang.org/u/Michael-F-Bryan)\
**Post date:** [September 7, 2020, 3:25pm UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/7 "2020-09-07T15:25:49Z")

</div>

Instead of proposing a new lang item that lets you use `TypeId` as a workaround for specialisation, why not just use specialisation directly?

```rust
#![feature(specialization)]

use std::borrow::Cow;

pub fn trusted<T>() -> bool {
    <T as MaybeTrusted>::IS_TRUSTED
}

trait MaybeTrusted {
    const IS_TRUSTED: bool;
}

impl<T> MaybeTrusted for T {
    default const IS_TRUSTED: bool = false;
}

impl<'a> MaybeTrusted for &'a str {
    const IS_TRUSTED: bool = true;
}

impl MaybeTrusted for String {
    const IS_TRUSTED: bool = true;
}

impl<'a> MaybeTrusted for Cow<'a, str> {
    const IS_TRUSTED: bool = true;
}

```

There's probably a better way though to achieve what you want without unstable features though... If some type is being used in a generic context then you already need to add trait bounds in order for it to be useful (unless you're doing `unsafe` things with pointers and raw memory, in which case "lifetime erasure" will almost certainly lead to a bad time).

And if that's the case, why not just roll your "trusted-ness" check into the function's trait bounds?

If you want to make sure downstream users can't override this check then you can use [the "sealed trait" pattern](https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed) ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=fd47b3de9c3ff49a3bb21084825ae39a)).

* * *

As an aside, this reminds me a lot of [`std::remove_cv`](https://en.cppreference.com/w/cpp/types/remove_cv) from C++. Lifetimes and `const`/`volatile` have a lot of similar properties and there are valid reasons to want to work with something regardless of cv-qualifiers in C++, so maybe it's worth providing people with similar tools in Rust?

---

<div class="post-metadata">

**Author:** ![waffle](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/waffle/32/14371_2.png) [@waffle](https://internals.rust-lang.org/u/waffle)\
**Post date:** [September 7, 2020, 5:42pm UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/8 "2020-09-07T17:42:13Z")

</div>

> [@Michael-F-Bryan](#):
>
> Instead of proposing a new lang item that lets you use `TypeId` as a workaround for specialisation, why not just use specialisation directly?

Specialization doesn't seem to be stabilized anytime soon, it's the only problem (with `nightly` feature I'm already using specialization...)

> [@Michael-F-Bryan](#):
>
> And if that's the case, why not just roll your "trusted-ness" check into the function's trait bounds?

This will lead to code duplication and user API pollution. E.g.:

```rust
impl MyType {
    fn new_trusted<S: Borrow<Str> + TrustedBorrow>(s: S) -> Self { ... }
    fn new_untrusted<S: Borrow<Str>>(s: S) -> Self { ... }
}

```

---

<div class="post-metadata">

**Author:** ![dhm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhm/32/4879_2.png) [@dhm](https://internals.rust-lang.org/u/dhm)\
**Post date:** [September 8, 2020, 11:45am UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/9 "2020-09-08T11:45:52Z")

</div>

Another API that may look less dangerous / error-prone and still enable your initial use-case would be to have:

```rust
fn maybe_type_id<T> () -> Option<TypeId>
{
    builtin!(if T : 'static { Some(TypeId::of::<T>()) } else { None })
}

```

It would make the idea that "I don't care about `TypeId` precise comparison in the non-`'static` case" clearer.

* * *

In practice, however, your use case is actually more of a `specialization` one.

- Granted, that one isn't stable yet (but that isn't an issue on Internals / when trying to ask for a new feature, since that new feature wouldn't be stable either), and for this case, `specialization` does carry many subtleties that your suggested simplification (especially further simplified to mine) does not. But I don't think that adding "intermediary features to save time" is an argument that holds in practice, since the main goal is to improve Rust in the long run.

* * *

Now, regarding the XY problem and the attempt to circumvent specialization,

> [@waffle](#):
>
> This will lead to code duplication and user API pollution. E.g.:
> 
> ```rust
> impl MyType {
> fn new_trusted<S: Borrow<Str> + TrustedBorrow>(s: S) -> Self { ... }
> fn new_untrusted<S: Borrow<Str>>(s: S) -> Self { ... }
> }
> 
> ```

This is something very common in the Rust ecosystem, so it should not be seen as bad, imho

- See, for instance, `Pin::new()` _vs._ `Pin::new_unchecked()`.

What you can do, **if you want downstream users to be able to `unsafe`-ly provide their own trustable types** , is try to change the API a bit, by making the function names express whether they perform a check or not, as is customary to do, and then provide an escape-hatch to opt-out of `unsafe` for the specific strings that can be trusted:

```rust
// Expected usage:
MyType::new(s, ...); // performs checks no matter what.

MyType::new_unchecked(s.into(), ...); // no need for `unsafe`
unsafe {
    MyType::new_unchecked(Trusted::trust(s), ...); // Trusted::trust needs `unsafe`
}

```

> **Implementation**
>
> Have the `_unchecked()` variant take a `Trusted<S> where S : Borrow<str>`:
> 
> ```rust
> pub
> struct Trusted<T> /* = */ (
> pub(in crate) T,
> );
> 
> impl<T> Trusted<T> {
> /// # Safety
> /// ...
> pub
> unsafe
> fn trust (it: T) -> Self
> {
> Self(it)
> }
> }
> 
> /// # Safety
> /// - it must be safe to call `Trustable::trust` on any instance
> /// of type `Self`.
> pub
> unsafe
> trait Trustable {}
> 
> impl<T : Trustable> From<T> for Trusted<T> {
> fn from (it: T) -> Self
> {
> unsafe {
> // Safety: `T : Trustable`
> Trusted::trust(it)
> }
> }
> }
> 
> ```

---

<div class="post-metadata">

**Author:** ![bill\_myers](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bill_myers/32/4085_2.png) [@bill\_myers](https://internals.rust-lang.org/u/bill_myers)\
**Post date:** [September 8, 2020, 11:55am UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/10 "2020-09-08T11:55:00Z")

</div>

Seems better to provide a variant of TypeId::of without the 'static bound.

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [September 8, 2020, 3:23pm UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/11 "2020-09-08T15:23:48Z")

</div>

While I have found myself wanting to erase lifetimes, that's only because I wanted to shove something into PhantomData without having to deal with the fact that the type might have lifetimes in it. The problem is that in Rust, the lifetime `'a` simply cannot be named outside of the corresponding region (which makes sense, since as they do not exist at runtime).

What I think would be much more useful is something like `MakeStatic<T>`, such that if `T` has lifetimes in it, then `MakeStatic<T>::Output` is `T` with all those lifetimes set to `'static`. Off the top of my head, I have no reason to believe there exists a type constructor of kind `K: lifetime -> type` such that `K<'static>` is an ill-formed type, but perhaps there's a contrived example involving contravariance.

Unfortunately, I don't believe such a thing could be implemented without compiler support.

---

<div class="post-metadata">

**Author:** ![dhm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhm/32/4879_2.png) [@dhm](https://internals.rust-lang.org/u/dhm)\
**Post date:** [September 9, 2020, 10:16am UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/12 "2020-09-09T10:16:17Z")

</div>

> [@bill\_myers](#):
>
> Seems better to provide a variant of TypeId::of without the 'static bound.

Sure, but the fact is, this cannot be done yet. Type-checking and "lifetime checking" (`borrowck`) are different things, meaning that the type information of `Thing<'a>` is the same as that of `Thing<'b>`, compiler-wise1, so they would have the _same_ `TypeId`. Thus:

- if `Any` lost its `'static` bound, that would be unsound;

- if it didn't, some people would circumvent that by reimplementing a new `Any` using that non-`'static`-ally-bound `TypeId::of`, and that would be unsound.

Hence my suggestion for a `TypeId::of` alternative, that would not require `'static`, but which would return `Option<TypeId>`, assuming, of course, a minimal capacity of the compiler type representation to be able to distinguish the `'static` case from _anything_ else.

* * *

1 To expand on this: IIUC, the compiler never sees an actual lifetime instance other than `'static`: once lifetime parameters are involved, `borrowck` handles that by creating _constraints_ on "regions" and trying to see just if solutions to the constrained problem could _exist_. But instances of an actual solution are never witnessed.

---

<div class="post-metadata">

**Author:** ![bill\_myers](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bill_myers/32/4085_2.png) [@bill\_myers](https://internals.rust-lang.org/u/bill_myers)\
**Post date:** [September 9, 2020, 2:26pm UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/13 "2020-09-09T14:26:35Z")

</div>

I mean, a variant of TypeId::of without a 'static bound that returns the same TypeId as if all lifetimes were replaced with 'static.

Obviously you can't use that to implement a safe downcasting API or Any variant, and if someone does their crate is broken.

---

<div class="post-metadata">

**Author:** ![mcy](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mcy/32/6512_2.png) [@mcy](https://internals.rust-lang.org/u/mcy)\
**Post date:** [September 9, 2020, 2:47pm UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/14 "2020-09-09T14:47:16Z")

</div>

The actual problem is the same reason we don't want to allow specialization on lifetimes: it allows program behavior to change based on inferred lifetimes.

The specific problem with `Any` is that:

- You cannot know exact lifetimes at runtime, only "this lifetime was at least as long as this other one" (a compile-time fact, really).
- You can't move the problem to compile time: lifetime variance makes something like `Any<'a>: 'a` (read: "any type that lives for as long as `'a`") not work. Let's elaborate on that.

* * *

Suppose we parametrize `Any` and `TypeId` by a lifetime, such that `Any<'a>` now represents "any type that outlives `'a`" (the current meaning is recovered by setting `'a = 'static`), so `T: 'a` implies `T: Any<'a>`. As before, all of `Any<'a>`'s impls are for `dyn Any<'a> + 'a`.

`TypeId::<'a>::of:<T>()` requires that `T: 'a`, and returns some value that may or may not incorporate `'a` into it (probably not, given the current implementation).

Here's where a problem might arise:

```rust
let x: &i32;
let any: &dyn Any<'a> + 'a = &x;
let any2: = &dyn Any<'static> + 'static = any;
let y = any2.downcast_ref::<&'static i32>();

```

This program is clearly incorrect, since we've changed an invariant lifetime. However, it seems that the conversion of `Any<'a>` into `Any<'b>` is not allowed: trait object lifetime parameters seem to always be invariant: [https://godbolt.org/z/3GEs7q](https://godbolt.org/z/3GEs7q)

Unfortunately, this isn't the whole picture. While `TypeId<'a>` and `dyn Any<'a>` can be invariant, the `impl` of `Any<'a>` cannot. That is, if we wrote

```rust
pub fn is<T: Any<'a>>(&self) -> bool {
  TypeId::<'a>::of::<T>() == self.type_id()
}

```

we have no way of ensuring that `T` does not have lifetimes in it that are _longer_ than those of `Self`. In general, I don't believe the lifetime formalism can express such a constraint.

See: [https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3abb9a4e777673f0344c6163b3dd1312](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3abb9a4e777673f0344c6163b3dd1312)

---

<div class="post-metadata">

**Author:** ![waffle](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/waffle/32/14371_2.png) [@waffle](https://internals.rust-lang.org/u/waffle)\
**Post date:** [September 10, 2020, 6:43am UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/15 "2020-09-10T06:43:08Z")

</div>

> [@dhm](#):
>
> What you can do, **if you want downstream users to be able to `unsafe` -ly provide their own trustable types** , is try to change the API a bit, by making the function names express whether they perform a check or not, as is customary to do, and then provide an escape-hatch to opt-out of `unsafe` for the specific strings that can be trusted:

That's a clever idea, thanks! Probably I'll use it.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [December 9, 2020, 6:43am UTC](https://internals.rust-lang.org/t/lifetime-erasure-type/13001/16 "2020-12-09T06:43:11Z")

</div>

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