Why are unused lifetimes okay in traits but not in structs/enums?

Why is

pub trait Trait<'a> {}

fn main() {}

fine, but

pub struct Struct<'a>;
pub enum Enum<'a> {}

fn main() {}

are not:

error[E0392]: lifetime parameter `'a` is never used
 --> .\test.rs:1:19
  |
1 | pub struct Struct<'a>;
  |                   ^^ unused lifetime parameter
  |
  = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`

error[E0392]: lifetime parameter `'a` is never used
 --> .\test.rs:2:15
  |
2 | pub enum Enum<'a> {}
  |               ^^ unused lifetime parameter
  |
  = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`

error: aborting due to 2 previous errors

?

Because in the case of Struct and Enum, the borrow checker needs to deal with instances of them, while in the case of Trait, an unused lifetime is not an issue unless it appears in the contract.

1 Like

Because it can be useful for marker traits (traits that add no functionality themselves, but are used to ensure that only types with certain permissions get through type checking, like Send and Unpin):

pub trait Trait<'a> {}

impl Trait<'static> for &'static str {}

impl<'a, 'b> Trait<'a> for &'b u32 {}

This gives us a situation where a function:

fn foo<'a, 'b, Input: Trait<'a> + 'b>(input: Input)

can only be called for a parameter of type &'static str if 'a: 'static and 'b: 'static, whereas it can be called for any &u32, regardless of lifetimes.

I can't, off the top of my head, think of a good reason to want this, but it's a feature that traits would otherwise lack.

I thought Rust's "philosophy" was that all types/traits/functions/etc. are type-checked immediately (vs. C++'s on-instantiation approach)?

That's confusing, but thanks anyway!

1 Like

Is it also because referencing a lifetime in a trait by adding a method would require implementing that method to use it, which is a bigger imposition than adding a private PhantomData field in the case of a struct?

AFAIK the reason is that types have variance, while traits don't. Given a struct Struct<'a> it is however not possible to determine whether it is covariant, contravariant or invariant with respect to 'a, so it is an error and you should specify that by introducing some field using it (e.g. PhantomData).

10 Likes

Variance (and the ability to infer it) is indeed the reason.

5 Likes

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.