Lifetime requirement on a type that is never instantiated

The following code gives a compiler error: error[E0310]: the parameter type F may not live long enough, but F is never used to declare a variable or a member or reference. It is only used to access its associated types.

trait A {
    type B: 'static;
}
struct C<D: A> {
    b: D::B
}

struct E<F: A> {
    x: &'static C<F>,
}

This must be some limitation of the type checker, but I can't figure out what exactly the problem is. Or is this a bug?

Also, this code compiles when I replace C<F> with F::B.

Then why not use PhantomData instead of a static reference?

use std::marker::PhantomData;

trait A {
    type B: 'static;
}
struct C<D: A> {
    b: D::B
}

struct E<F: A> {
    x: PhantomData<C<F>>,
}

Playground.

One detail to point out:
When the requirement above, setting type B = &'a T; doesn't just require 'a: 'static, but also T: 'static

I still need that reference to F::B (which is wrapped in the struct C<F> and as I pointed out it works when I use &F::B directly)

I'm not worried about B, my problem is that F needs 'static for some reason.

Ah, I missed that point. If you have &'static T then T must also be static for the type to be valid

Can't have &'static &'a str, for example

I'm sorry, but I don't follow how using a &'static C<F> would be different from PhantomData<C<F>> when you want to access the associated type B of F. Could you please provide an example for me where the former works but the latter doesn't?

It's a combination of a few things:

  • &'static C<F> is invalid unless C<F>: 'static
  • C<F>: 'static requires F: 'static
  • Unlike other cases, the compiler doesn't infer the required bound (F: 'static)

The last bullet is a limitation or shortcoming (you can just add the bound yourself); the first two have soundness considerations.

This is the one that baffles me. For my type C only has a field of type F::B. So I understand that for C<F>: 'static it would require F::B: 'static. Why would F need to be 'static when it is never instantiated?

impl<F: A> E<F> {
  fn new(v: C<F>) -> E<F> {
    E{ x: Box::leak(Box::new(v)) }
  }
}

enum ImplA {}
impl A for ImplA {
  type B = usize;
}

fn main() {
  let example = E::<ImplA>::new(C{ b: 41 });
  println!("{}", example.x.b + 1);
}

Here example.x must be a &'static C<ImplA>. It cannot be a PhantomData<C<ImplA>>.

1 Like

Ah, I see. I misunderstood your statement as to mean you only need x for F::B, but of course, why would you wrap it in C then. My bad.

Without that requirement, you can erase or transmute any lifetimes in F in various ways (coerce to dyn Trait + 'static, round-trip through Box<dyn Any>) which is unsound in the general case.

There's more motivations in the RFC that made the rule syntactical.

https://rust-lang.github.io/rfcs/1214-projections-lifetimes-and-wf.html

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.